From 07e9402f0c517c655a3b4904028938fca468a3e6 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 1 Feb 2021 07:42:20 +1100 Subject: [PATCH 001/125] fix(windows): Refresh setings on 64-bit apps Fixes #4289. Some settings changes would not apply immediately for 64-bit apps, as the refresh was not being processed until keymanx64 received focus (i.e. never, as keymanx64 does not have a visible window!) --- windows/src/engine/keyman32/Keyman32.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/windows/src/engine/keyman32/Keyman32.cpp b/windows/src/engine/keyman32/Keyman32.cpp index 3b7eb03d1e..560b2060ea 100644 --- a/windows/src/engine/keyman32/Keyman32.cpp +++ b/windows/src/engine/keyman32/Keyman32.cpp @@ -726,8 +726,17 @@ void HandleRefresh(int code, LONG tag) // refreshed after an update, but only once per // refresh request if (UpdateRefreshTag(tag)) { - // We'll update when we are called into action - ScheduleRefresh(); +#ifdef _WIN64 + if (Globals::get_InitialisingThread() == GetCurrentThreadId()) { + // If this is the keymanx64 thread, then we should + // go ahead and process the refresh immediately so + // that global settings are updated + RefreshKeyboards(FALSE); + } + else +#endif + // We'll update when we are called into action + ScheduleRefresh(); } break; } @@ -755,7 +764,7 @@ void LoadBaseLayoutSettings() { // I4552 // I4583 Globals::SetBaseKeyboardFlags(underlyingLayout, reg->ReadInteger(REGSZ_SimulateAltGr), !reg->ValueExists(REGSZ_DeadkeyConversionMode) || reg->ReadInteger(REGSZ_DeadkeyConversionMode)); } else { - Globals::SetBaseKeyboardFlags("", 0, 0); + Globals::SetBaseKeyboardFlags("", 0, 1); } delete reg; From ca7ab8a7d800364a52f46213c92320544dab7541 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 2 Feb 2021 14:55:02 +1100 Subject: [PATCH 002/125] fix(windows): improve cross-process refresh Two changes here: 1. Only post the refresh message to the master controller, so that we get a single refresh regardless of how many controllers are registered. 2. When Keyman is restarted, the global RefreshTag is reset to 0, so we need to do a `!=` comparison rather than a `<` comparison to ensure that apps with an existing keyman32.dll/keyman64.dll in memory get refresh notifications (as their local `RefreshTag_Process` will likely already be greater than `0`). Also a small additional debug statement. --- windows/src/engine/keyman32/Keyman32.cpp | 13 ++--- windows/src/engine/keyman32/k32_globals.cpp | 58 +++++++++++---------- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/windows/src/engine/keyman32/Keyman32.cpp b/windows/src/engine/keyman32/Keyman32.cpp index 560b2060ea..1ee4fae749 100644 --- a/windows/src/engine/keyman32/Keyman32.cpp +++ b/windows/src/engine/keyman32/Keyman32.cpp @@ -679,7 +679,7 @@ BOOL UpdateRefreshTag(LONG tag) // I1835 - Reduce chatter { PKEYMAN64THREADDATA _td = ThreadGlobals(); if(!_td) return FALSE; - if(_td->RefreshTag_Process < tag) + if(_td->RefreshTag_Process != tag) { _td->RefreshTag_Process = tag; return TRUE; @@ -695,9 +695,9 @@ void HandleRefresh(int code, LONG tag) // This is sent by Keyman COM API, ApplyToRunningKeymanEngine SendDebugMessageFormat(0, sdmGlobal, 0, "#### Refresh Requested ####"); - // We ask any controller window to tell all instances of keyman32/keyman64 - // that a refresh is coming through - Globals::PostControllers(wm_keyman_refresh, KR_PRE_REFRESH, 0); + // We ask the master controller window to tell all instances + // of keyman32/keyman64 that a refresh is coming through + Globals::PostMasterController(wm_keyman_refresh, KR_PRE_REFRESH, 0); // We need to tell the controller windows to refresh themselves also Globals::PostControllers(wm_keyman_control, KMC_REFRESH, 0); @@ -709,8 +709,9 @@ void HandleRefresh(int code, LONG tag) // a double-broadcast which could happen if both keyman32 and keyman64 // receive the message, as they have independently managed RefreshTags - // All controllers will receive this message; only one need act on it - tag = InterlockedIncrement(Globals::RefreshTag()); + // This RefreshTag is only ever incremented here by the master controller + // so this is a thread safe operation + tag = ++(*Globals::RefreshTag()); if (UpdateRefreshTag(tag)) { // The Keyman process gets the update first diff --git a/windows/src/engine/keyman32/k32_globals.cpp b/windows/src/engine/keyman32/k32_globals.cpp index cb7a71f69f..dc4b68de36 100644 --- a/windows/src/engine/keyman32/k32_globals.cpp +++ b/windows/src/engine/keyman32/k32_globals.cpp @@ -1,18 +1,18 @@ /* Name: k32_globals Copyright: Copyright (C) SIL International. - Documentation: - Description: + Documentation: + Description: Create Date: 1 Aug 2006 Modified Date: 6 Feb 2015 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 01 Aug 2006 - mcdurdin - Add hotkeys globals 14 Sep 2006 - mcdurdin - Add IsKeymanSystemWindow and UpdateActiveWindows functions 04 Dec 2006 - mcdurdin - Add PostProcessControllers and IsControllerProcess functions @@ -69,7 +69,7 @@ /* */ /***************************************************************************/ -UINT +UINT //TODO: consolidate these messages -- they are probably not all required now wm_keyman = 0, // user message - ignore msg // I3594 wm_keyman_keyevent = 0, // for serialized input @@ -86,7 +86,7 @@ UINT wm_keymanim_close = 0, wm_keymanim_contextchanged = 0, - + wm_test_keyman_functioning = 0; //HWND hwndIM = 0, hwndIMOldFocus = 0; @@ -100,7 +100,7 @@ static CRITICAL_SECTION csGlobals; PKEYMAN64THREADDATA Globals_InitThread() { EnterCriticalSection(&csGlobals); - + PKEYMAN64THREADDATA lpvData; if(Globals::get_Keyman_Shutdown()) // I3092 @@ -190,7 +190,7 @@ void Globals_UninitProcess() PKEYMAN64THREADDATA ThreadGlobals() { EnterCriticalSection(&csGlobals); - + PKEYMAN64THREADDATA lpvData = NULL; if(dwTlsIndex != TLS_OUT_OF_INDEXES) { @@ -220,7 +220,7 @@ BOOL Globals_ProcessInitialised() //static INI // I3158 // I3524 // f_Ini = {0}; // KEYMAN.INI options -static HHOOK +static HHOOK f_hhookGetMessage = NULL, // GETMESSAGE hook handle f_hhookCallWndProc = NULL; // CALLWNDPROC hook handle @@ -236,7 +236,7 @@ static HWND static UINT f_WindowsVersion = 0; // Windows Version -static BOOL +static BOOL f_Keyman_Initialised = FALSE, f_Keyman_Shutdown = FALSE; @@ -280,14 +280,14 @@ __declspec(align(8)) static LONG f_RefreshTag = 0; #endif -static BOOL - f_debug_KeymanLog = FALSE, +static BOOL + f_debug_KeymanLog = FALSE, f_debug_ToConsole = FALSE; #pragma data_seg() /***************************************************************************/ -DWORD //static +DWORD //static f_ShiftState = 0; HANDLE f_hLockMutex = 0; @@ -364,13 +364,15 @@ void Globals::SetBaseKeyboardName(wchar_t *baseKeyboardName, wchar_t *baseKeyboa } void Globals::SetBaseKeyboardFlags(char *baseKeyboard, BOOL simulateAltGr, BOOL mnemonicDeadkeyConversionMode) { // I4583 // I4552 + SendDebugMessageFormat(0, sdmAIDefault, 0, "Globals::SetBaseKeyboardFlags(baseKeyboard='%s', simulateAltGr=%d, mnemonicDeadkeyConversionMode=%d", + baseKeyboard, simulateAltGr, mnemonicDeadkeyConversionMode); strcpy_s(f_BaseKeyboard, baseKeyboard); f_SimulateAltGr = simulateAltGr; f_MnemonicDeadkeyConversionMode = mnemonicDeadkeyConversionMode; } /** - Loads settings that are globally applied at application startup. These cannot + Loads settings that are globally applied at application startup. These cannot be changed until Keyman is restarted. */ BOOL Globals::InitSettings() { @@ -392,7 +394,7 @@ BOOL Globals::Lock() f_hLockMutex = CreateMutex(NULL, FALSE, "Tavultesoft_KeymanEngine_GlobalLock"); if(f_hLockMutex == 0) return FALSE; } - + if(WaitForSingleObject(f_hLockMutex, 1000) == WAIT_OBJECT_0) return TRUE; // I3183 // I3530 SetLastError(ERROR_TIMEOUT); // I3183 // I3530 return FALSE; // I3183 // I3530 @@ -400,7 +402,7 @@ BOOL Globals::Lock() BOOL Globals::InitHandles() // I3040 { - if(f_hLockMutex == 0) + if(f_hLockMutex == 0) f_hLockMutex = CreateMutex(NULL, FALSE, "Tavultesoft_KeymanEngine_GlobalLock"); if(f_hLockMutex == 0) return FALSE; SetObjectToLowIntegrity(f_hLockMutex); @@ -477,7 +479,7 @@ void Globals::PostProcessControllers(UINT msg, WPARAM wParam, LPARAM lParam) for(int i = 0; i < f_MaxControllers; i++) { GetWindowThreadProcessId(f_ControllerWindow[i], &pid); - if(pid == GetCurrentProcessId()) + if(pid == GetCurrentProcessId()) PostMessage(f_ControllerWindow[i], msg, wParam, lParam); } Unlock(); @@ -492,7 +494,7 @@ BOOL Globals::IsControllerProcess() { GetWindowThreadProcessId(f_ControllerWindow[i], &pid); if(pid == GetCurrentProcessId()) - { + { Unlock(); return TRUE; } @@ -524,7 +526,7 @@ void Globals::PostMasterController(UINT msg, WPARAM wParam, LPARAM lParam) Unlock(); return; } - + if(ShouldDebug(sdmGlobal)) { char buf[64]; @@ -552,7 +554,7 @@ LRESULT Globals::SendMasterController(UINT msg, WPARAM wParam, LPARAM lParam) HWND hwnd = f_ControllerWindow[0]; Unlock(); - + if(ShouldDebug(sdmGlobal)) { char buf[64]; @@ -561,7 +563,7 @@ LRESULT Globals::SendMasterController(UINT msg, WPARAM wParam, LPARAM lParam) SendDebugMessageFormat(f_ControllerWindow[0], sdmGlobal, 0, "SendMasterController %s [%x : %x, %x]", buf, msg, wParam, lParam); } - // Window may be taken out of list between Lock and Unlock but let's not worry about this + // Window may be taken out of list between Lock and Unlock but let's not worry about this SetLastError(ERROR_SUCCESS); DWORD_PTR dwResult = 0; @@ -633,7 +635,7 @@ BOOL IsKeymanSystemWindow(HWND hwnd) DWORD pidC, pidHWND; GetWindowThreadProcessId(hwnd, &pidHWND); - + if(!Globals::Lock()) return FALSE; for(int i = 0; i < f_MaxControllers; i++) @@ -641,7 +643,7 @@ BOOL IsKeymanSystemWindow(HWND hwnd) if(f_ControllerWindow[i] == hwnd) { Globals::Unlock(); - return TRUE; + return TRUE; } GetWindowThreadProcessId(f_ControllerWindow[i], &pidC); if(pidC == pidHWND) @@ -666,10 +668,10 @@ void UpdateActiveWindows() GUITHREADINFO gti; gti.cbSize = sizeof(GUITHREADINFO); GetGUIThreadInfo(0, >i); - + if(IsKeymanSystemWindow(gti.hwndActive) || IsKeymanSystemWindow(gti.hwndFocus)) return; if(IsTooltipWindow(gti.hwndActive) || IsTooltipWindow(gti.hwndFocus)) return; - + *Globals::hLastActiveWnd() = gti.hwndActive; *Globals::hLastFocusWnd() = gti.hwndFocus; From 38500871094f910ec5ffdb69344aa0d0b29b4ff2 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 2 Feb 2021 14:58:59 +1100 Subject: [PATCH 003/125] fix(windows): rename RefreshTag_Process variable `RefreshTag_Process` should be named `RefreshTag_Thread`. --- windows/src/engine/keyman32/Keyman32.cpp | 4 ++-- windows/src/engine/keyman32/globals.h | 28 ++++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/windows/src/engine/keyman32/Keyman32.cpp b/windows/src/engine/keyman32/Keyman32.cpp index 1ee4fae749..7f7476e249 100644 --- a/windows/src/engine/keyman32/Keyman32.cpp +++ b/windows/src/engine/keyman32/Keyman32.cpp @@ -679,9 +679,9 @@ BOOL UpdateRefreshTag(LONG tag) // I1835 - Reduce chatter { PKEYMAN64THREADDATA _td = ThreadGlobals(); if(!_td) return FALSE; - if(_td->RefreshTag_Process != tag) + if(_td->RefreshTag_Thread != tag) { - _td->RefreshTag_Process = tag; + _td->RefreshTag_Thread = tag; return TRUE; } return FALSE; diff --git a/windows/src/engine/keyman32/globals.h b/windows/src/engine/keyman32/globals.h index 80d90f2fc2..19d8025c3c 100644 --- a/windows/src/engine/keyman32/globals.h +++ b/windows/src/engine/keyman32/globals.h @@ -1,18 +1,18 @@ /* Name: globals Copyright: Copyright (C) SIL International. - Documentation: - Description: + Documentation: + Description: Create Date: 1 Aug 2006 Modified Date: 25 Oct 2016 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 01 Aug 2006 - mcdurdin - Add hotkeys 14 Sep 2006 - mcdurdin - Add UpdateActiveWindows function 16 May 2007 - mcdurdin - I819 - Test if Keyman is functioning @@ -83,7 +83,7 @@ public: static DWORD *FSingleThread(); static DWORD *InitialisingThread(); // I4326 - + //static DWORD *ActiveKeymanID(); //static HKL *ActiveKeyboardLayout(); @@ -98,7 +98,7 @@ public: static HHOOK get_hhookGetMessage(); static HHOOK get_hhookCallWndProc(); - + #ifndef _WIN64 static HHOOK get_hhookLowLevelKeyboardProc(); // I4124 #endif @@ -134,7 +134,7 @@ public: static LRESULT SendMasterController(UINT msg, WPARAM wParam, LPARAM lParam); static void PostMasterController(UINT msg, WPARAM wParam, LPARAM lParam); - + static BOOL ResetControllers(); static void PostControllers(UINT msg, WPARAM wParam, LPARAM lParam); @@ -220,12 +220,12 @@ typedef struct tagKEYMAN64THREADDATA PKEYMANPROCESSOUTPUTFUNC TIPProcessOutput; PKEYMANGETCONTEXTFUNC TIPGetContext; - + BOOL TIPFUpdateable, TIPFPreserved; // I4290 BOOL FInRefreshKeyboards; BOOL RefreshRequired; - LONG RefreshTag_Process; // TODO: we may be able to eliminate this with our delayed refresh pattern? + LONG RefreshTag_Thread; // TODO: we may be able to eliminate this with our delayed refresh pattern? /* Addin Globals */ @@ -254,7 +254,7 @@ typedef struct tagKEYMAN64THREADDATA } KEYMAN64THREADDATA, *PKEYMAN64THREADDATA; -extern UINT +extern UINT wm_keyman, // user message - ignore msg wm_kmdebug, // " " " " - debugging wm_keyman_control, // messages to main Keyman window - replaces WM_USER+* @@ -269,7 +269,7 @@ extern UINT wm_keymanim_close, wm_keymanim_contextchanged, wm_test_keyman_functioning, - wm_keyman_keyevent; // for serialized input + wm_keyman_keyevent; // for serialized input extern BOOL flag_ShouldSerializeInput; From e50596335ecc645f5098f81a4050a6b3a908ff20 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 3 Feb 2021 10:58:41 +0700 Subject: [PATCH 004/125] fix(common/models): adds wordbreak text only at end of context --- common/predictive-text/worker/model-compositor.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/predictive-text/worker/model-compositor.ts b/common/predictive-text/worker/model-compositor.ts index 80a0e5bcfe..133dbf3357 100644 --- a/common/predictive-text/worker/model-compositor.ts +++ b/common/predictive-text/worker/model-compositor.ts @@ -306,7 +306,12 @@ class ModelCompositor { // Valid 'keep' suggestions may have zero length; we still need to evaluate the following code // for such cases. - suggestion.transform.insert += punctuation.insertAfterWord; + if(!context.right) { + // Only insert wordbreak characters if we're at the end of the context. + suggestion.transform.insert += punctuation.insertAfterWord; + } else { + // TODO: Replace all characters in the word to the right of the caret. + } // If this is a suggestion after wordbreak input, make sure we preserve the wordbreak transform! if(prefixTransform) { From 138a7db228f4edb06b8f220e4a9a8559ff2ba895 Mon Sep 17 00:00:00 2001 From: jahorton Date: Fri, 5 Feb 2021 09:16:06 +0700 Subject: [PATCH 005/125] feat(common/models): mid-context suggestion acceptance --- .../worker/model-compositor.ts | 22 +++++++++++++++++-- .../Classes/InputViewController.swift | 13 ++++++++--- .../Classes/KeymanWebDelegate.swift | 2 +- .../Classes/KeymanWebViewController.swift | 8 +++---- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/common/predictive-text/worker/model-compositor.ts b/common/predictive-text/worker/model-compositor.ts index 133dbf3357..9ece746699 100644 --- a/common/predictive-text/worker/model-compositor.ts +++ b/common/predictive-text/worker/model-compositor.ts @@ -306,11 +306,18 @@ class ModelCompositor { // Valid 'keep' suggestions may have zero length; we still need to evaluate the following code // for such cases. + + // Do we need to manipulate the suggestion's transform based on the current state of the context? if(!context.right) { // Only insert wordbreak characters if we're at the end of the context. suggestion.transform.insert += punctuation.insertAfterWord; } else { - // TODO: Replace all characters in the word to the right of the caret. + // If we're mid-word, delete its original post-caret text. + const tokenization = compositor.tokenize(context); + if(tokenization && tokenization.caretSplitsToken) { + let righthandSplit = tokenization.right[0]; + suggestion.transform.deleteRight = (suggestion.transform.deleteRight || 0) + righthandSplit.kmwLength(); + } } // If this is a suggestion after wordbreak input, make sure we preserve the wordbreak transform! @@ -421,11 +428,12 @@ class ModelCompositor { // Step 1: generate and save the reversion's Transform. let sourceTransform = suggestion.transform; let deletedLeftChars = context.left.kmwSubstr(-sourceTransform.deleteLeft, sourceTransform.deleteLeft); + let deletedRightChars = context.right.kmwSubstr(0, sourceTransform.deleteRight || 0); // right deletion is currently not implemented. let insertedLength = sourceTransform.insert.kmwLength(); let reversionTransform: Transform = { - insert: deletedLeftChars, + insert: deletedLeftChars + deletedRightChars, deleteLeft: insertedLength }; @@ -540,6 +548,16 @@ class ModelCompositor { } } + private tokenize(context: Context): models.Tokenization { + let model = this.lexicalModel; + + if(model.wordbreaker) { + return models.tokenize(model.wordbreaker, context); + } else { + return null; + } + } + public resetContext(context: Context) { // Force-resets the context, throwing out any previous fat-finger data, etc. // Designed for use when the caret has been directly moved and/or the context sourced from a different control diff --git a/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift index 87d84bb7d1..e60bbf4ea5 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift @@ -338,7 +338,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { // We should NOT call .resetContext() here for this reason. } - func insertText(_ keymanWeb: KeymanWebViewController, numCharsToDelete: Int, newText: String) { + func insertText(_ keymanWeb: KeymanWebViewController, numCharsToLeftDelete: Int, newText: String, numCharsToRightDelete: Int) { if keymanWeb.isSubKeysMenuVisible { return } @@ -351,7 +351,14 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { perform(#selector(self.enableInputClickSound), with: nil, afterDelay: 0.1) } - if numCharsToDelete <= 0 { + if numCharsToRightDelete > 0 { + for _ in 0.. 1 { - if !InputViewController.isSurrogate(oldContext.utf16.last!) { - let lowerIndex = oldContext.utf16.index(oldContext.utf16.startIndex, - offsetBy: newContext.utf16.count) - let upperIndex = oldContext.utf16.index(lowerIndex, offsetBy: unitsDeleted - 1) - textDocumentProxy.insertText(String(oldContext[lowerIndex.. 1 { + if !InputViewController.isSurrogate(oldContext.utf16.last!) { + let lowerIndex = oldContext.utf16.index(oldContext.utf16.startIndex, + offsetBy: newContext.utf16.count) + let upperIndex = oldContext.utf16.index(lowerIndex, offsetBy: unitsDeleted - 1) + let remnant = String(oldContext.utf16[lowerIndex.. 1 { + if !InputViewController.isSurrogate(oldContext.utf16.first!) { + let lowerIndex = oldContext.utf16.index(oldContext.utf16.startIndex, + offsetBy: 1) + let upperIndex = oldContext.utf16.index(lowerIndex, offsetBy: unitsDeleted - 1) + let remnant = String(oldContext.utf16[lowerIndex.. Date: Wed, 10 Feb 2021 10:52:32 +0700 Subject: [PATCH 011/125] fix(common/models): correction-search smp issues --- common/models/templates/src/common.ts | 8 ++++---- common/predictive-text/worker/model-compositor.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common/models/templates/src/common.ts b/common/models/templates/src/common.ts index babd2cf247..38b0d5f08f 100644 --- a/common/models/templates/src/common.ts +++ b/common/models/templates/src/common.ts @@ -4,16 +4,16 @@ namespace models { export function applyTransform(transform: Transform, context: Context): Context { // First, get the current context let fullLeftContext = context.left || ''; - let lLen = fullLeftContext.length; + let lLen = fullLeftContext.kmwLength(); let lDel = lLen < transform.deleteLeft ? lLen : transform.deleteLeft; - let leftContext = fullLeftContext.substring(0, lLen - lDel) + (transform.insert || ''); + let leftContext = fullLeftContext.kmwSubstr(0, lLen - lDel) + (transform.insert || ''); let fullRightContext = context.right || ''; - let rLen = fullRightContext.length; + let rLen = fullRightContext.kmwLength(); let rDel = rLen < transform.deleteRight ? rLen : transform.deleteRight; - let rightContext = fullRightContext.substring(rDel); + let rightContext = fullRightContext.kmwSubstr(rDel); return { left: leftContext, diff --git a/common/predictive-text/worker/model-compositor.ts b/common/predictive-text/worker/model-compositor.ts index 48d42e1a73..fa47525f1d 100644 --- a/common/predictive-text/worker/model-compositor.ts +++ b/common/predictive-text/worker/model-compositor.ts @@ -179,7 +179,7 @@ class ModelCompositor { let correctionTransform: Transform = { insert: correction, // insert correction string // remove actual token string. If new token, there should be nothing to delete. - deleteLeft: newEmptyToken ? 0 : this.wordbreak(context).length, + deleteLeft: newEmptyToken ? 0 : this.wordbreak(context).kmwLength(), id: inputTransform.id // The correction should always be based on the most recent external transform/transcription ID. } From 08a9f0ae8a08ee94bd5ee34697dd915ce1305e24 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 10 Feb 2021 10:53:33 +0700 Subject: [PATCH 012/125] feat(android): in-app right deletions --- .../java/com/tavultesoft/kmea/KMManager.java | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 9365a1bb15..72610c2f92 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -36,6 +36,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.text.Editable; import android.text.InputType; import android.util.Log; import android.view.KeyEvent; @@ -2351,10 +2352,6 @@ public final class KMManager { // This annotation is required in Jelly Bean and later: @JavascriptInterface public void insertText(final int dn, final String s, final int dr) { - if(dr != 0) { - Log.d(TAG, "Right deletions requested but are not presently supported by the in-app keyboard."); - } - Handler mainLoop = new Handler(Looper.getMainLooper()); mainLoop.post(new Runnable() { public void run() { @@ -2386,6 +2383,28 @@ public final class KMManager { start = temp; } + // As we depend upon the caret's current position for the in-app implementation, + // we need to perform right-deletions BEFORE left-deletions & text insertion. + if(dr > 0) { + for (int i = 0; i < dr; i++) { + Editable context = textView.getText(); + CharSequence chars = context.subSequence(end, context.length()); + if (chars != null && chars.length() > 0) { + char c = chars.charAt(0); + InAppKeyboardShouldIgnoreTextChange = true; + InAppKeyboardShouldIgnoreSelectionChange = true; + if (Character.isHighSurrogate(c)) { + textView.getText().delete(start, end+2); + } else { + textView.getText().delete(start, end+1); + } + + start = textView.getSelectionStart(); + end = textView.getSelectionEnd(); + } + } + } + if (dn <= 0) { if (start == end) { if (!s.isEmpty() && s.charAt(0) == '\n') { From e14b689239f7da2c61222fc9d53b682a372a31bd Mon Sep 17 00:00:00 2001 From: jahorton Date: Fri, 12 Feb 2021 09:56:35 +0700 Subject: [PATCH 013/125] fix(ios): nixes source of (some) duplicate context resets --- ios/engine/KMEI/KeymanEngine/Classes/TextView.swift | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/TextView.swift b/ios/engine/KMEI/KeymanEngine/Classes/TextView.swift index 87fe2fde28..e120d530ad 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/TextView.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/TextView.swift @@ -114,13 +114,6 @@ public class TextView: UITextView, KeymanResponder { } } - public override var selectedTextRange: UITextRange? { - didSet { - Manager.shared.setContextState(text: self.text, range: selectedRange) - Manager.shared.resetContext() - } - } - // MARK: - Keyman notifications private func keyboardChanged(_ kb: InstallableKeyboard) { if !shouldSetCustomFontOnKeyboardChange { From d574d1e23c7909c31c3e3200e5bf7ae4f3f4c597 Mon Sep 17 00:00:00 2001 From: jahorton Date: Fri, 12 Feb 2021 09:57:18 +0700 Subject: [PATCH 014/125] fix(common/models): better right-deletion logic --- .../Classes/InputViewController.swift | 106 +++++++++++++++--- 1 file changed, 92 insertions(+), 14 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift index dba500e81d..6742c7cdc7 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift @@ -168,6 +168,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { private var keymanWeb: KeymanWebViewController private var swallowBackspaceTextChange: Bool = false + private var swallowContextChangeCount: Int = 0 open class var isPortrait: Bool { return UIScreen.main.bounds.width < UIScreen.main.bounds.height @@ -321,6 +322,13 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { self.swallowBackspaceTextChange = false return } + + // Currently motivated by the need to shift caret position to perform + // right-deletions. + if self.swallowContextChangeCount > 0 { + self.swallowContextChangeCount -= 1 + return + } let contextBeforeInput = textDocumentProxy.documentContextBeforeInput ?? "" let contextAfterInput = textDocumentProxy.documentContextAfterInput ?? "" @@ -399,26 +407,94 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { textDocumentProxy.insertText(newText) } - // TODO: should probably 'swallow' caret position shifts throughout this block. - for _ in 0.. 0 { let oldContext = textDocumentProxy.documentContextAfterInput ?? "" + self.swallowContextChangeCount += 1 + // Asynchronously triggers a context change. textDocumentProxy.adjustTextPosition(byCharacterOffset: 1) - textDocumentProxy.deleteBackward() let newContext = textDocumentProxy.documentContextAfterInput ?? "" - let unitsDeleted = oldContext.utf16.count - newContext.utf16.count - if unitsDeleted > 1 { - if !InputViewController.isSurrogate(oldContext.utf16.first!) { - let lowerIndex = oldContext.utf16.index(oldContext.utf16.startIndex, - offsetBy: 1) - let upperIndex = oldContext.utf16.index(lowerIndex, offsetBy: unitsDeleted - 1) - let remnant = String(oldContext.utf16[lowerIndex.. locked kbd + // While adjustTextPosition makes cluster-based jumps... + // deleteBackward does not. So, we 'force' it. + textDocumentProxy.deleteBackward() + failsafeCount += 1 + } - // Must place the caret back in its correct position! - textDocumentProxy.adjustTextPosition(byCharacterOffset: -remnant.count) + // Determine the removed codepoint count. Also, find the index + // of the first character that shouldn't be removed, if possible. + let unitsDeleted = oldContext.utf16.count - newContext.utf16.count + var pointsDeleted = 0 + + // Our current index within the deleted context. + var deletedIndex = oldContext.utf16.startIndex + // Marks the first index we wish to NOT remove. + var remnantIndex: String.Index? = nil + // Marks the first index that was NOT removed by deleteBackward. + let undeletedStartIndex = oldContext.utf16.index(deletedIndex, offsetBy: unitsDeleted) + + while deletedIndex < undeletedStartIndex { + if(InputViewController.isSurrogate(oldContext.utf16[deletedIndex])) { + // Check - is it truly a surrogate pair? + let pairedIndex = oldContext.utf16.index(after: deletedIndex) + if(InputViewController.isSurrogate(oldContext.utf16[pairedIndex])) { + // If so, pre-emptively increase the index - the pair will count + // as a single character as a result. + deletedIndex = pairedIndex + } + } + pointsDeleted += 1 + deletedIndex = oldContext.utf16.index(after: deletedIndex) + + // Intended end of deletion found! + if pointsDeleted == pointsToDelete { + remnantIndex = deletedIndex + break // There's nothing to be gained by further loop iterations. } } + + if let remnantIndex = remnantIndex, remnantIndex < undeletedStartIndex { + // We need to restore some of the deleted text! + let remnant = String(oldContext.utf16[remnantIndex.. oldLeftContext.count) { + textDocumentProxy.adjustTextPosition(byCharacterOffset: -remnant.count) + leftContext = textDocumentProxy.documentContextBeforeInput ?? "" + } + + // FAILSAFE, non-ideal behavior below. + // + // It's probably (?) better to keep the caret at the end of a cluster that + // previously started before the caret, even if right-deletions add new + // characters to the cluster. I think. + // + // Khmer example: if right-deletes leave a joeung-S that can attach + // to a main consonant to the left of my cursor, it will 'snap' into + // a cluster. It'd be more natural to have that cluster on the left, + // so I can add more chars to the cluster if desired. + // + // It's also easier to immediately backspace the extra chars if needed + // this way, rather than forcing a caret reposition. + if(leftContext.count < oldLeftContext.count) { + log.debug("Could not reposition caret perfectly after a right-delete!") + textDocumentProxy.adjustTextPosition(byCharacterOffset: 1) + } + } + + // We've handled a set of code points; make sure we mark our progress! + pointsToDelete -= pointsDeleted } } @@ -571,7 +647,9 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { } func resetContext() { - keymanWeb.resetContext() + if self.swallowContextChangeCount <= 0 { + keymanWeb.resetContext() + } } internal func setSentryState(enabled: Bool) { From 6b5b8784638fc7a5c66f922792d9b85d6336fcdf Mon Sep 17 00:00:00 2001 From: jahorton Date: Fri, 12 Feb 2021 13:43:58 +0700 Subject: [PATCH 015/125] chore(ios): web-side sentry enablement try-catch --- .../KMEI/KeymanEngine/Classes/KeymanWebViewController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift index a4f4ae626a..e20c4f7112 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift @@ -366,7 +366,9 @@ extension KeymanWebViewController { * thereafter for settings updates. */ func setSentryState(enabled: Bool = SentryManager.enabled) { - webView?.evaluateJavaScript("sentryManager.enabled = \(enabled ? "true" : "false")") + // This may be called before the page - and thus the `sentryManager` variable - + // is ready. It's a known limitation, so why log error reports for it? + webView?.evaluateJavaScript("try { sentryManager.enabled = \(enabled ? "true" : "false") } catch(err) { }") } } From 96ad4ea12d18ec58236e2ce289f4a4357eaf9f32 Mon Sep 17 00:00:00 2001 From: jahorton Date: Mon, 22 Feb 2021 08:43:03 +0700 Subject: [PATCH 016/125] fix(web): macOS 11 agent string parsing --- web/source/kmwdevice.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/web/source/kmwdevice.ts b/web/source/kmwdevice.ts index 99d28fb247..2e8ade5adc 100644 --- a/web/source/kmwdevice.ts +++ b/web/source/kmwdevice.ts @@ -84,11 +84,14 @@ namespace com.keyman { // // Firefox uses '.' between version components, while Chrome and Safari use // '_' instead. So, we have to check for both. Yay. - let regex = /Intel Mac OS X (10(?:[_\.]\d+)+)/i; + let regex = /Intel Mac OS X (\d+(?:[_\.]\d+)+)/i; let results = regex.exec(agent); // Match result: a version string with components separated by underscores. - if(results.length > 1 && results[1]) { + if(!results) { + console.warn("KMW could not properly parse the user agent string." + + "A suboptimal keyboard layout may result."); + } else if(results.length > 1 && results[1]) { // Convert version string into a usable form. let versionString = results[1].replace('_', '.'); let version = new utils.Version(versionString); From 331d6958b442dc8f6b043b585e971b21b1729d2d Mon Sep 17 00:00:00 2001 From: jahorton Date: Mon, 22 Feb 2021 11:55:24 +0700 Subject: [PATCH 017/125] fix(ios): log messages were Xcode-only --- ios/engine/KMEI/KeymanEngine/Classes/Log.swift | 17 ++++++++++++++++- ios/keyman/Keyman/Keyman/Log.swift | 17 ++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Log.swift b/ios/engine/KMEI/KeymanEngine/Classes/Log.swift index 92cf9e28f2..d6683f4d62 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Log.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Log.swift @@ -8,4 +8,19 @@ import XCGLogger -public let log = XCGLogger(identifier: "KeymanEngine", includeDefaultDestinations: true) +public let log: XCGLogger = { + // Default: the 'console', which is read by Xcode but doesn't reach the system logs. + let mainLog = XCGLogger(identifier: "KeymanEngine", includeDefaultDestinations: true) + + // Ensures our log messages go out to the device's system log. + let systemLogDest = AppleSystemLogDestination(identifier: "") + systemLogDest.showLogIdentifier = true +#if DEBUG + systemLogDest.outputLevel = .debug +#else + systemLogDest.outputLevel = .warning +#endif + mainLog.add(destination: systemLogDest) + + return mainLog +}() diff --git a/ios/keyman/Keyman/Keyman/Log.swift b/ios/keyman/Keyman/Keyman/Log.swift index 1ba2871845..38a511865d 100644 --- a/ios/keyman/Keyman/Keyman/Log.swift +++ b/ios/keyman/Keyman/Keyman/Log.swift @@ -8,4 +8,19 @@ import XCGLogger -let log = XCGLogger(identifier: "Keyman", includeDefaultDestinations: true) +public let log: XCGLogger = { + // Default: the 'console', which is read by Xcode but doesn't reach the system logs. + let mainLog = XCGLogger(identifier: "Keyman", includeDefaultDestinations: true) + + // Ensures our log messages go out to the device's system log. + let systemLogDest = AppleSystemLogDestination(identifier: "") + systemLogDest.showLogIdentifier = true +#if DEBUG + systemLogDest.outputLevel = .debug +#else + systemLogDest.outputLevel = .warning +#endif + mainLog.add(destination: systemLogDest) + + return mainLog +}() From 6d0e9af4b82f8d8060ab60527576c5e3d4658d40 Mon Sep 17 00:00:00 2001 From: jahorton Date: Mon, 22 Feb 2021 12:04:46 +0700 Subject: [PATCH 018/125] fix(web): OS assignment fail-safe --- web/source/kmwdevice.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/web/source/kmwdevice.ts b/web/source/kmwdevice.ts index 2e8ade5adc..1338de4dd8 100644 --- a/web/source/kmwdevice.ts +++ b/web/source/kmwdevice.ts @@ -91,6 +91,7 @@ namespace com.keyman { if(!results) { console.warn("KMW could not properly parse the user agent string." + "A suboptimal keyboard layout may result."); + this.OS='MacOSX'; } else if(results.length > 1 && results[1]) { // Convert version string into a usable form. let versionString = results[1].replace('_', '.'); From aa724804699661187e8d2be4b533a5eeebe346e5 Mon Sep 17 00:00:00 2001 From: jahorton Date: Mon, 22 Feb 2021 13:29:28 +0700 Subject: [PATCH 019/125] change(ios): more log centralization --- .../KMEI/KeymanEngine/Classes/Log.swift | 22 +++++++++++++------ .../KMEI/KeymanEngineDemo/AppDelegate.swift | 2 -- .../KeyboardViewController.swift | 2 -- ios/keyman/Keyman/Keyman/AppDelegate.swift | 13 ++++------- ios/keyman/Keyman/Keyman/Log.swift | 22 +++++++++++++------ .../SWKeyboard/KeyboardViewController.swift | 6 ----- 6 files changed, 34 insertions(+), 33 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Log.swift b/ios/engine/KMEI/KeymanEngine/Classes/Log.swift index d6683f4d62..7f8e47e413 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Log.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Log.swift @@ -8,19 +8,27 @@ import XCGLogger +// From XCGLogger docs: +// Note: This creates the log object lazily, which means it's not created until it's actually needed. public let log: XCGLogger = { // Default: the 'console', which is read by Xcode but doesn't reach the system logs. - let mainLog = XCGLogger(identifier: "KeymanEngine", includeDefaultDestinations: true) + let mainLog = XCGLogger(identifier: "KeymanEngine", includeDefaultDestinations: false) - // Ensures our log messages go out to the device's system log. + // Ensures our log messages go out to the device's system log as well as the console. let systemLogDest = AppleSystemLogDestination(identifier: "") systemLogDest.showLogIdentifier = true -#if DEBUG - systemLogDest.outputLevel = .debug -#else - systemLogDest.outputLevel = .warning -#endif + mainLog.add(destination: systemLogDest) + // Temporary logging level to ensure that app details are reported properly. + mainLog.outputLevel = .info + mainLog.logAppDetails() + +#if DEBUG + mainLog.outputLevel = .debug +#else + mainLog.outputLevel = .warning +#endif + return mainLog }() diff --git a/ios/engine/KMEI/KeymanEngineDemo/AppDelegate.swift b/ios/engine/KMEI/KeymanEngineDemo/AppDelegate.swift index 1e643d76f0..9eb651ca01 100644 --- a/ios/engine/KMEI/KeymanEngineDemo/AppDelegate.swift +++ b/ios/engine/KMEI/KeymanEngineDemo/AppDelegate.swift @@ -15,8 +15,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? = nil) -> Bool { - KeymanEngine.log.outputLevel = .debug - KeymanEngine.log.logAppDetails() Manager.applicationGroupIdentifier = "group.KMEI" Manager.shared.canRemoveDefaultKeyboard = true diff --git a/ios/engine/KMEI/SystemKeyboard/KeyboardViewController.swift b/ios/engine/KMEI/SystemKeyboard/KeyboardViewController.swift index 9a54cdcc8e..9152cead27 100644 --- a/ios/engine/KMEI/SystemKeyboard/KeyboardViewController.swift +++ b/ios/engine/KMEI/SystemKeyboard/KeyboardViewController.swift @@ -10,8 +10,6 @@ import KeymanEngine class KeyboardViewController: InputViewController { override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { - KeymanEngine.log.outputLevel = .debug - KeymanEngine.log.logAppDetails() Manager.applicationGroupIdentifier = "group.KMEI" super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } diff --git a/ios/keyman/Keyman/Keyman/AppDelegate.swift b/ios/keyman/Keyman/Keyman/AppDelegate.swift index c5ce2f46d5..bb09f19918 100644 --- a/ios/keyman/Keyman/Keyman/AppDelegate.swift +++ b/ios/keyman/Keyman/Keyman/AppDelegate.swift @@ -54,20 +54,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { SentryManager.start() + // Forces the logs to initialize, as their definitions result in lazy init. + // These references have been configured to also log app details. + _ = log + _ = KeymanEngine.log UniversalLinks.externalLinkLauncher = { url in UIApplication.shared.openURL(url) } - #if DEBUG - KeymanEngine.log.outputLevel = .debug - log.outputLevel = .debug - KeymanEngine.log.logAppDetails() - #else - KeymanEngine.log.outputLevel = .warning - log.outputLevel = .warning - #endif - Manager.applicationGroupIdentifier = "group.KM4I" // TODO: Assign a subclassed version of InputViewController that implements the image stuff. diff --git a/ios/keyman/Keyman/Keyman/Log.swift b/ios/keyman/Keyman/Keyman/Log.swift index 38a511865d..c074921570 100644 --- a/ios/keyman/Keyman/Keyman/Log.swift +++ b/ios/keyman/Keyman/Keyman/Log.swift @@ -8,19 +8,27 @@ import XCGLogger +// From XCGLogger docs: +// Note: This creates the log object lazily, which means it's not created until it's actually needed. public let log: XCGLogger = { // Default: the 'console', which is read by Xcode but doesn't reach the system logs. - let mainLog = XCGLogger(identifier: "Keyman", includeDefaultDestinations: true) + let mainLog = XCGLogger(identifier: "Keyman", includeDefaultDestinations: false) - // Ensures our log messages go out to the device's system log. + // Ensures our log messages go out to the device's system log as well as the console. let systemLogDest = AppleSystemLogDestination(identifier: "") systemLogDest.showLogIdentifier = true -#if DEBUG - systemLogDest.outputLevel = .debug -#else - systemLogDest.outputLevel = .warning -#endif + mainLog.add(destination: systemLogDest) + // Temporary logging level to ensure that app details are reported properly. + mainLog.outputLevel = .info + mainLog.logAppDetails() + +#if DEBUG + mainLog.outputLevel = .debug +#else + mainLog.outputLevel = .warning +#endif + return mainLog }() diff --git a/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift b/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift index 8e716607bb..88e1cba3f8 100644 --- a/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift +++ b/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift @@ -22,12 +22,6 @@ class KeyboardViewController: InputViewController { SentryManager.start(sendingEnabled: true) } - #if DEBUG - KeymanEngine.log.outputLevel = .debug - KeymanEngine.log.logAppDetails() - #else - KeymanEngine.log.outputLevel = .warning - #endif Manager.applicationGroupIdentifier = "group.KM4I" let bundle = Bundle(for: KeyboardViewController.self) From 57f37c8c45b9be9fc15e845d97d6c67c29244393 Mon Sep 17 00:00:00 2001 From: jahorton Date: Mon, 22 Feb 2021 13:31:38 +0700 Subject: [PATCH 020/125] fix(ios): restores a log reference --- ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift b/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift index 88e1cba3f8..0c5a2dcdb3 100644 --- a/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift +++ b/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift @@ -21,6 +21,8 @@ class KeyboardViewController: InputViewController { // is enabled. They seem to get blocked otherwise, except in the Simulator. SentryManager.start(sendingEnabled: true) } + _ = log + _ = KeymanEngine.log Manager.applicationGroupIdentifier = "group.KM4I" From 66949ed89d564be376ff3b7e31fd64c15a3f22bd Mon Sep 17 00:00:00 2001 From: jahorton Date: Mon, 22 Feb 2021 13:32:47 +0700 Subject: [PATCH 021/125] fix(ios): one more sys-kbd log ref --- ios/engine/KMEI/SystemKeyboard/KeyboardViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ios/engine/KMEI/SystemKeyboard/KeyboardViewController.swift b/ios/engine/KMEI/SystemKeyboard/KeyboardViewController.swift index 9152cead27..8cf3c85e5a 100644 --- a/ios/engine/KMEI/SystemKeyboard/KeyboardViewController.swift +++ b/ios/engine/KMEI/SystemKeyboard/KeyboardViewController.swift @@ -10,6 +10,7 @@ import KeymanEngine class KeyboardViewController: InputViewController { override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + _ = log // forces init of the log, which is useful in sys-kbd contexts. Manager.applicationGroupIdentifier = "group.KMEI" super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } From 21d9f49001e24c6a2945cfdacfa4c65d5033d44a Mon Sep 17 00:00:00 2001 From: jahorton Date: Mon, 22 Feb 2021 14:43:58 +0700 Subject: [PATCH 022/125] fix(common/models): merges identical suggestions after casing --- .../worker/model-compositor.ts | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/common/predictive-text/worker/model-compositor.ts b/common/predictive-text/worker/model-compositor.ts index f27bf50b31..eb80540467 100644 --- a/common/predictive-text/worker/model-compositor.ts +++ b/common/predictive-text/worker/model-compositor.ts @@ -220,10 +220,15 @@ class ModelCompositor { } // Section 2 - post-analysis for our generated predictions, managing 'keep'. - // Assumption: Duplicated 'displayAs' properties indicate duplicated Suggestions. // When true, we can use an 'associative array' to de-duplicate everything. let suggestionDistribMap: {[key: string]: ProbabilityMass} = {}; + let currentCasing: CasingForm = null; + if(lexicalModel.languageUsesCasing) { + currentCasing = this.detectCurrentCasing(postContext); + } + + let baseWord = this.wordbreak(context); // Deduplicator + annotator of 'keep' suggestions. for(let prediction of rawPredictions) { @@ -253,6 +258,16 @@ class ModelCompositor { keepOption.p += prediction.p; } } else { + // Apply capitalization rules now; facilitates de-duplication of suggestions + // that may be caused as a result. + // + // Example: "apple" and "Apple" are separate when 'lower', but identical for 'initial' and 'upper'. + if(currentCasing && currentCasing != 'lower') { + this.applySuggestionCasing(prediction.sample, baseWord, currentCasing); + // update the mapping string, too. + displayText = prediction.sample.displayAs; + } + let existingSuggestion = suggestionDistribMap[displayText]; if(existingSuggestion) { existingSuggestion.p += prediction.p; @@ -306,18 +321,9 @@ class ModelCompositor { // Apply 'after word' punctuation and casing (when applicable). Also, set suggestion IDs. // We delay until now so that utility functions relying on the unmodified Transform may execute properly. - let currentCasing: CasingForm = null; - if(lexicalModel.languageUsesCasing) { - currentCasing = this.detectCurrentCasing(postContext); - } let compositor = this; - let baseWord = this.wordbreak(context); suggestions.forEach(function(suggestion) { - if(currentCasing && currentCasing != 'lower') { - compositor.applySuggestionCasing(suggestion, baseWord, currentCasing); - } - // Valid 'keep' suggestions may have zero length; we still need to evaluate the following code // for such cases. suggestion.transform.insert += punctuation.insertAfterWord; From 7b3a9b3a2bcc24911b167300b4a5836fd56e73e4 Mon Sep 17 00:00:00 2001 From: jahorton Date: Mon, 22 Feb 2021 16:24:53 +0700 Subject: [PATCH 023/125] fix(developer): fixes error when "constructor" is in wordlist --- .../js/source/lexical-model-compiler/build-trie.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/developer/js/source/lexical-model-compiler/build-trie.ts b/developer/js/source/lexical-model-compiler/build-trie.ts index a64abab7a4..7703327bb1 100644 --- a/developer/js/source/lexical-model-compiler/build-trie.ts +++ b/developer/js/source/lexical-model-compiler/build-trie.ts @@ -138,7 +138,7 @@ function _parseWordList(wordlist: WordList, source: WordListSource): void { } wordsSeenInThisFile.add(wordform); - wordlist[wordform] = (wordlist[wordform] || 0) + count; + wordlist[wordform] = (isNaN(wordlist[wordform]) ? 0 : wordlist[wordform] || 0) + count; } } @@ -459,15 +459,22 @@ namespace Trie { * @param node The node to start summing weights. */ function sumWeights(node: Node): number { + let val: number; if (node.type === 'leaf') { - return node.entries + val = node.entries .map(entry => entry.weight) + //.map(entry => isNaN(entry.weight) ? 1 : entry.weight) .reduce((acc, count) => acc + count, 0); } else { - return Object.keys(node.children) + val = Object.keys(node.children) .map((key) => sumWeights(node.children[key])) .reduce((acc, count) => acc + count, 0); } + + if(isNaN(val)) { + console.error("Unexpected NaN has appeared!"); + } + return val; } } From ba097f074c2686dd1f5ff03f68b10a00ffba1381 Mon Sep 17 00:00:00 2001 From: Keyman Build Agent Date: Mon, 22 Feb 2021 13:02:42 -0500 Subject: [PATCH 024/125] auto: increment beta version to 14.0.245 --- HISTORY.md | 7 +++++++ VERSION.md | 2 +- .../web/input-processor/package-lock.json | 2 +- common/core/web/input-processor/package.json | 16 +++++++-------- .../web/keyboard-processor/package-lock.json | 2 +- .../core/web/keyboard-processor/package.json | 10 +++++----- .../core/web/tools/recorder/package-lock.json | 2 +- common/core/web/tools/recorder/package.json | 10 +++++----- .../tools/sentry-manager/package-lock.json | 2 +- .../web/tools/sentry-manager/package.json | 4 ++-- common/core/web/utils/package-lock.json | 2 +- common/core/web/utils/package.json | 6 +++--- common/models/templates/package-lock.json | 2 +- common/models/templates/package.json | 8 ++++---- common/models/types/package-lock.json | 2 +- common/models/types/package.json | 2 +- common/models/wordbreakers/package-lock.json | 2 +- common/models/wordbreakers/package.json | 4 ++-- common/predictive-text/package-lock.json | 2 +- common/predictive-text/package.json | 14 ++++++------- developer/js/package-lock.json | 2 +- developer/js/package.json | 8 ++++---- lerna.json | 2 +- resources/gosh/package-lock.json | 2 +- resources/gosh/package.json | 2 +- resources/web-environment/package-lock.json | 2 +- resources/web-environment/package.json | 4 ++-- web/package-lock.json | 2 +- web/package.json | 20 +++++++++---------- 29 files changed, 76 insertions(+), 69 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index fc8a2e2bee..e37d70b981 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,12 @@ # Keyman Version History +## 14.0.244 beta 2021-02-22 + +* fix(common/models): merges identical suggestions after casing (#4502) +* fix(web): macOS 11 agent string parsing (#4497) +* fix(ios): app logging messages were transient, never stored (#4500) +* chore(ios): web-side sentry enablement try-catch (#4492) + ## 14.0.243 beta 2021-02-12 * change(ios/app): Generate offline help from markdown (#4470) diff --git a/VERSION.md b/VERSION.md index 127a9bb58b..ae6cc4dc1f 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -14.0.244 \ No newline at end of file +14.0.245 \ No newline at end of file diff --git a/common/core/web/input-processor/package-lock.json b/common/core/web/input-processor/package-lock.json index 8983adc1c3..1daec2df01 100644 --- a/common/core/web/input-processor/package-lock.json +++ b/common/core/web/input-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.244", + "version": "14.0.245", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/input-processor/package.json b/common/core/web/input-processor/package.json index e6b1070e8c..4225959dda 100644 --- a/common/core/web/input-processor/package.json +++ b/common/core/web/input-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.244", + "version": "14.0.245", "description": "The core text and prediction processing engine for KeymanWeb", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/lexical-model-compiler": "^14.0.244", - "@keymanapp/resources-gosh": "^14.0.244", + "@keymanapp/lexical-model-compiler": "^14.0.245", + "@keymanapp/resources-gosh": "^14.0.245", "@types/node": "^11.9.4", "chai": "^4.2.0", "mocha": "^5.2.0", @@ -34,11 +34,11 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.244", - "@keymanapp/lexical-model-layer": "^14.0.244", - "@keymanapp/models-types": "^14.0.244", - "@keymanapp/web-environment": "^14.0.244", - "@keymanapp/web-utils": "^14.0.244", + "@keymanapp/keyboard-processor": "^14.0.245", + "@keymanapp/lexical-model-layer": "^14.0.245", + "@keymanapp/models-types": "^14.0.245", + "@keymanapp/web-environment": "^14.0.245", + "@keymanapp/web-utils": "^14.0.245", "eventemitter3": "^4.0.0" } } diff --git a/common/core/web/keyboard-processor/package-lock.json b/common/core/web/keyboard-processor/package-lock.json index db081f86cd..8b76e56b1d 100644 --- a/common/core/web/keyboard-processor/package-lock.json +++ b/common/core/web/keyboard-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.244", + "version": "14.0.245", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/keyboard-processor/package.json b/common/core/web/keyboard-processor/package.json index bb4ae3cc82..86113e19b1 100644 --- a/common/core/web/keyboard-processor/package.json +++ b/common/core/web/keyboard-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.244", + "version": "14.0.245", "description": "Core module for Keyman keyboard support in KeymanWeb.", "repository": { "type": "git", @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.244", + "@keymanapp/resources-gosh": "^14.0.245", "chai": "^4.2.0", "mocha": "^5.2.0", "mocha-teamcity-reporter": "^3.0.0", @@ -31,9 +31,9 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/models-types": "^14.0.244", - "@keymanapp/web-environment": "^14.0.244", - "@keymanapp/web-utils": "^14.0.244", + "@keymanapp/models-types": "^14.0.245", + "@keymanapp/web-environment": "^14.0.245", + "@keymanapp/web-utils": "^14.0.245", "@types/node": "^11.9.4", "ts-node": "^8.0.2" } diff --git a/common/core/web/tools/recorder/package-lock.json b/common/core/web/tools/recorder/package-lock.json index 49d4e531b5..c0875fbf8f 100644 --- a/common/core/web/tools/recorder/package-lock.json +++ b/common/core/web/tools/recorder/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.244", + "version": "14.0.245", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/recorder/package.json b/common/core/web/tools/recorder/package.json index 826ad85d38..de46b8bc5b 100644 --- a/common/core/web/tools/recorder/package.json +++ b/common/core/web/tools/recorder/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.244", + "version": "14.0.245", "description": "Core classes used to develop KeymanWeb test cases based on keystrokes", "main": "index.js", "scripts": { @@ -17,10 +17,10 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.244", - "@keymanapp/models-types": "^14.0.244", - "@keymanapp/web-environment": "^14.0.244", - "@keymanapp/web-utils": "^14.0.244" + "@keymanapp/keyboard-processor": "^14.0.245", + "@keymanapp/models-types": "^14.0.245", + "@keymanapp/web-environment": "^14.0.245", + "@keymanapp/web-utils": "^14.0.245" }, "devDependencies": { "typescript": "^3.8.3" diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json index e86ca542a5..249309ffc7 100644 --- a/common/core/web/tools/sentry-manager/package-lock.json +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.244", + "version": "14.0.245", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json index c3341219a3..f846d8718e 100644 --- a/common/core/web/tools/sentry-manager/package.json +++ b/common/core/web/tools/sentry-manager/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.244", + "version": "14.0.245", "description": "Common setup for KeymanWeb's Sentry-based error reporting", "main": "dist/index.js", "scripts": { @@ -20,7 +20,7 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/web-environment": "^14.0.244", + "@keymanapp/web-environment": "^14.0.245", "@sentry/browser": "^5.27.4" } } diff --git a/common/core/web/utils/package-lock.json b/common/core/web/utils/package-lock.json index 59e9f1e7b0..dc9ae7beef 100644 --- a/common/core/web/utils/package-lock.json +++ b/common/core/web/utils/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.244", + "version": "14.0.245", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/utils/package.json b/common/core/web/utils/package.json index 0b9a1d90da..091fe867f9 100644 --- a/common/core/web/utils/package.json +++ b/common/core/web/utils/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.244", + "version": "14.0.245", "description": "Common utility functions used throughout other Keyman packages", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -20,8 +20,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.244", - "@keymanapp/web-environment": "^14.0.244", + "@keymanapp/resources-gosh": "^14.0.245", + "@keymanapp/web-environment": "^14.0.245", "@types/node": "^14.0.5", "typescript": "^3.8.3" } diff --git a/common/models/templates/package-lock.json b/common/models/templates/package-lock.json index 759ba13d2e..ca3d9a436e 100644 --- a/common/models/templates/package-lock.json +++ b/common/models/templates/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.244", + "version": "14.0.245", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/templates/package.json b/common/models/templates/package.json index 9c385b1bec..625f8fa579 100644 --- a/common/models/templates/package.json +++ b/common/models/templates/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.244", + "version": "14.0.245", "description": "Backing model implementations (templates) for Keyman's modelling layer", "keywords": [ "lm", @@ -42,8 +42,8 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.244", - "@keymanapp/web-utils": "^14.0.244", + "@keymanapp/models-types": "^14.0.245", + "@keymanapp/web-utils": "^14.0.245", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "@types/node": "^14.0.4", @@ -52,6 +52,6 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-wordbreakers": "^14.0.244" + "@keymanapp/models-wordbreakers": "^14.0.245" } } diff --git a/common/models/types/package-lock.json b/common/models/types/package-lock.json index 906a545c3c..9b368635b5 100644 --- a/common/models/types/package-lock.json +++ b/common/models/types/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.244", + "version": "14.0.245", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/types/package.json b/common/models/types/package.json index ee3e6cd7bc..373c988ad2 100644 --- a/common/models/types/package.json +++ b/common/models/types/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.244", + "version": "14.0.245", "description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.", "types": "./index.d.ts", "scripts": { diff --git a/common/models/wordbreakers/package-lock.json b/common/models/wordbreakers/package-lock.json index 31d272c7cb..d257af3b48 100644 --- a/common/models/wordbreakers/package-lock.json +++ b/common/models/wordbreakers/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.244", + "version": "14.0.245", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json index bc4e8d3ac4..28ddc332fa 100644 --- a/common/models/wordbreakers/package.json +++ b/common/models/wordbreakers/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.244", + "version": "14.0.245", "description": "Word breakers bundled in the modeling layer", "keywords": [ "lexical model", @@ -40,7 +40,7 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.244", + "@keymanapp/models-types": "^14.0.245", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "chai": "^4.2.0", diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index 4641580f73..e70e9bb7d6 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.244", + "version": "14.0.245", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json index 128596acc4..4869f091b3 100644 --- a/common/predictive-text/package.json +++ b/common/predictive-text/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.244", + "version": "14.0.245", "description": "Keyman/Predictive Text integration layer", "main": "build/index.js", "scripts": { @@ -21,8 +21,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/models-types": "^14.0.244", - "@keymanapp/resources-gosh": "^14.0.244", + "@keymanapp/models-types": "^14.0.245", + "@keymanapp/resources-gosh": "^14.0.245", "@types/node": "^10.17.21", "chai": "^4.2.0", "karma": "^4.2.0", @@ -46,10 +46,10 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-templates": "^14.0.244", - "@keymanapp/models-wordbreakers": "^14.0.244", - "@keymanapp/web-environment": "^14.0.244", - "@keymanapp/web-utils": "^14.0.244", + "@keymanapp/models-templates": "^14.0.245", + "@keymanapp/models-wordbreakers": "^14.0.245", + "@keymanapp/web-environment": "^14.0.245", + "@keymanapp/web-utils": "^14.0.245", "es6-shim": "^0.35.5", "string.prototype.codepointat": "^0.2.1", "string.prototype.startswith": "^0.2.0" diff --git a/developer/js/package-lock.json b/developer/js/package-lock.json index 85431706c6..b167eabd0c 100644 --- a/developer/js/package-lock.json +++ b/developer/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.244", + "version": "14.0.245", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/developer/js/package.json b/developer/js/package.json index 4f3ba33081..593d7bd510 100644 --- a/developer/js/package.json +++ b/developer/js/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.244", + "version": "14.0.245", "description": "Keyman Developer lexical model compiler", "keywords": [ "keyboard", @@ -37,14 +37,14 @@ "node": ">=12.0.0" }, "dependencies": { - "@keymanapp/models-types": "^14.0.244", + "@keymanapp/models-types": "^14.0.245", "commander": "^3.0.0", "typescript": "^3.8.3", "xml2js": "^0.4.19" }, "devDependencies": { - "@keymanapp/models-templates": "^14.0.244", - "@keymanapp/models-wordbreakers": "^14.0.244", + "@keymanapp/models-templates": "^14.0.245", + "@keymanapp/models-wordbreakers": "^14.0.245", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", diff --git a/lerna.json b/lerna.json index df1a8d0eba..3996dd8687 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "lerna", "typescript" ], - "version": "14.0.244" + "version": "14.0.245" } diff --git a/resources/gosh/package-lock.json b/resources/gosh/package-lock.json index 35b9152d58..66c12b8755 100644 --- a/resources/gosh/package-lock.json +++ b/resources/gosh/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.244", + "version": "14.0.245", "lockfileVersion": 1 } diff --git a/resources/gosh/package.json b/resources/gosh/package.json index 9ea00ea4ec..1dd0259f8c 100644 --- a/resources/gosh/package.json +++ b/resources/gosh/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.244", + "version": "14.0.245", "description": "Good Ol' Shell", "author": "Marc Durdin (https://github.com/mcdurdin)", "contributors": [ diff --git a/resources/web-environment/package-lock.json b/resources/web-environment/package-lock.json index 4be9143b07..7fc9c7f015 100644 --- a/resources/web-environment/package-lock.json +++ b/resources/web-environment/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.244", + "version": "14.0.245", "lockfileVersion": 1 } diff --git a/resources/web-environment/package.json b/resources/web-environment/package.json index 994a86402a..1cc33039c8 100644 --- a/resources/web-environment/package.json +++ b/resources/web-environment/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.244", + "version": "14.0.245", "description": "A compilation utility for passing select environment variables into our TS builds", "author": "Joshua Horton (https://github.com/jahorton)", "homepage": "https://github.com/keymanapp/keyman", @@ -17,6 +17,6 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.244" + "@keymanapp/resources-gosh": "^14.0.245" } } diff --git a/web/package-lock.json b/web/package-lock.json index 8d7fb44991..56b523095e 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.244", + "version": "14.0.245", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/web/package.json b/web/package.json index 34f5aa8313..559695f87a 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.244", + "version": "14.0.245", "description": "Facilitates text input in any language.", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.244", - "@keymanapp/web-sentry-manager": "^14.0.244", + "@keymanapp/resources-gosh": "^14.0.245", + "@keymanapp/web-sentry-manager": "^14.0.245", "@sentry/cli": "^1.52.3", "chai": "^4.2.0", "google-closure-compiler-java": "^20200224.0.0", @@ -51,13 +51,13 @@ "sentry-cli": "sentry-cli" }, "dependencies": { - "@keymanapp/input-processor": "^14.0.244", - "@keymanapp/keyboard-processor": "^14.0.244", - "@keymanapp/lexical-model-layer": "^14.0.244", - "@keymanapp/models-types": "^14.0.244", - "@keymanapp/recorder-core": "^14.0.244", - "@keymanapp/web-environment": "^14.0.244", - "@keymanapp/web-utils": "^14.0.244", + "@keymanapp/input-processor": "^14.0.245", + "@keymanapp/keyboard-processor": "^14.0.245", + "@keymanapp/lexical-model-layer": "^14.0.245", + "@keymanapp/models-types": "^14.0.245", + "@keymanapp/recorder-core": "^14.0.245", + "@keymanapp/web-environment": "^14.0.245", + "@keymanapp/web-utils": "^14.0.245", "@types/node": "^11.9.4", "es6-shim": "^0.35.5", "eventemitter3": "^4.0.0", From f20388d956c10242f6a3cea2a024f89974769379 Mon Sep 17 00:00:00 2001 From: jahorton Date: Tue, 23 Feb 2021 11:55:06 +0700 Subject: [PATCH 025/125] fix(common/core/web): core key-processing returns --- .../web/input-processor/src/text/inputProcessor.ts | 10 +++++----- .../web/keyboard-processor/src/text/ruleBehavior.ts | 8 ++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/common/core/web/input-processor/src/text/inputProcessor.ts b/common/core/web/input-processor/src/text/inputProcessor.ts index d5f7813fe9..72b7bb44b4 100644 --- a/common/core/web/input-processor/src/text/inputProcessor.ts +++ b/common/core/web/input-processor/src/text/inputProcessor.ts @@ -56,7 +56,7 @@ namespace com.keyman.text { * * @param {Object} e The abstracted KeyEvent to use for keystroke processing */ - processKeyEvent(keyEvent: KeyEvent) { + processKeyEvent(keyEvent: KeyEvent): RuleBehavior { let formFactor = keyEvent.device.formFactor; // Determine the current target for text output and create a "mock" backup @@ -70,14 +70,14 @@ namespace com.keyman.text { // If it's a desktop OSK style and this triggers a layer change, // a modifier key was clicked. No output expected, so it's safe to instantly exit. if(this.keyboardProcessor.selectLayer(keyEvent)) { - return true; + return new RuleBehavior(); } } // Will handle keystroke-based non-layer change modifier & state keys, mapping them through the physical keyboard's version // of state management. if(!fromOSK && this.keyboardProcessor.doModifierPress(keyEvent, !fromOSK)) { - return true; + return new RuleBehavior(); } // If suggestions exist AND space is pressed, accept the suggestion and do not process the keystroke. @@ -88,10 +88,10 @@ namespace com.keyman.text { // Can the suggestion UI revert a recent suggestion? If so, do that and swallow the backspace. if((keyEvent.kName == "K_BKSP" || keyEvent.Lcode == Codes.keyCodes["K_BKSP"]) && this.languageProcessor.tryRevertSuggestion()) { - return; + return new RuleBehavior(); // Can the suggestion UI accept an existing suggestion? If so, do that and swallow the space character. } else if((keyEvent.kName == "K_SPACE" || keyEvent.Lcode == Codes.keyCodes["K_SPACE"]) && this.languageProcessor.tryAcceptSuggestion('space')) { - return; + return new RuleBehavior(); } } diff --git a/common/core/web/keyboard-processor/src/text/ruleBehavior.ts b/common/core/web/keyboard-processor/src/text/ruleBehavior.ts index 012cb44c97..10657b2404 100644 --- a/common/core/web/keyboard-processor/src/text/ruleBehavior.ts +++ b/common/core/web/keyboard-processor/src/text/ruleBehavior.ts @@ -6,7 +6,7 @@ namespace com.keyman.text { /** * The before-and-after Transform from matching a keyboard rule. */ - transcription: Transcription; + transcription: Transcription = null; /** * Indicates whether or not a BEEP command was issued by the matched keyboard rule. @@ -26,7 +26,7 @@ namespace com.keyman.text { /** * Denotes a non-output default behavior; this should be evaluated later, against the true keystroke. */ - triggersDefaultCommand?: boolean; + triggersDefaultCommand: boolean = false; /** * Denotes error log messages generated when attempting to generate this behavior. @@ -44,6 +44,10 @@ namespace com.keyman.text { predictionPromise?: Promise; finalize(processor: KeyboardProcessor) { + if(!this.transcription) { + throw "Cannot finalize a RuleBehavior with no transcription."; + } + let outputTarget = this.transcription.keystroke.Ltarg; if(processor.beepHandler && this.beep) { From 4618dce6a0ec424cec50bc4d92c8fae7043cb281 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 24 Feb 2021 09:02:26 +0700 Subject: [PATCH 026/125] docs(common/core/web): addresses PR review comment --- .../web/input-processor/src/text/inputProcessor.ts | 11 ++--------- .../web/keyboard-processor/src/text/ruleBehavior.ts | 3 ++- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/common/core/web/input-processor/src/text/inputProcessor.ts b/common/core/web/input-processor/src/text/inputProcessor.ts index 72b7bb44b4..759dccc8d0 100644 --- a/common/core/web/input-processor/src/text/inputProcessor.ts +++ b/common/core/web/input-processor/src/text/inputProcessor.ts @@ -55,6 +55,8 @@ namespace com.keyman.text { * Handles default output and keyboard processing for both OSK and physical keystrokes. * * @param {Object} e The abstracted KeyEvent to use for keystroke processing + * @returns {Object} A RuleBehavior object describing the cumulative effects of + * all matched keyboard rules. */ processKeyEvent(keyEvent: KeyEvent): RuleBehavior { let formFactor = keyEvent.device.formFactor; @@ -171,15 +173,6 @@ namespace com.keyman.text { } } - /* I732 END - 13/03/2007 MCD: End Positional Layout support in OSK */ - - // TODO: rework the return value to be `ruleBehavior` instead. Functions that call this one are - // the ones that should worry about event handler returns, etc. Not this one. - // - // They should also be the ones to handle the TODOs seen earlier in this function - - // once THOSE are properly relocated. (They're too DOM-heavy to remain in web-core.) - - // Only return true (for the eventual event handler's return value) if we didn't match a rule. return ruleBehavior; } diff --git a/common/core/web/keyboard-processor/src/text/ruleBehavior.ts b/common/core/web/keyboard-processor/src/text/ruleBehavior.ts index 10657b2404..7532abd90b 100644 --- a/common/core/web/keyboard-processor/src/text/ruleBehavior.ts +++ b/common/core/web/keyboard-processor/src/text/ruleBehavior.ts @@ -4,7 +4,8 @@ namespace com.keyman.text { */ export class RuleBehavior { /** - * The before-and-after Transform from matching a keyboard rule. + * The before-and-after Transform from matching a keyboard rule. May be `null` + * if no keyboard rules were matched for the keystroke. */ transcription: Transcription = null; From cf758452c0502bcb50f0eadf02188ed30dceefa1 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Wed, 24 Feb 2021 09:35:40 +0700 Subject: [PATCH 027/125] fix(common): Set help-keyman.com.sh executable --- resources/build/help-keyman-com.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 resources/build/help-keyman-com.sh diff --git a/resources/build/help-keyman-com.sh b/resources/build/help-keyman-com.sh old mode 100644 new mode 100755 From 567df44a1aa1867dc96d6f02ce73900adabdd09c Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 24 Feb 2021 12:10:37 +0700 Subject: [PATCH 028/125] fix(web): mobile layouts for static keyboards now %-based --- web/source/osk/visualKeyboard.ts | 38 ++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 7e1e3fc14b..eaf4f61827 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -170,7 +170,7 @@ namespace com.keyman.osk { } getKeyWidth(osk: VisualKeyboard): number { - let units = this.objectUnits(); + let units = this.objectUnits(osk.isStatic); if(units == 'px') { // For mobile devices, we presently specify width directly in pixels. Just use that! @@ -184,9 +184,9 @@ namespace com.keyman.osk { } } - objectUnits(): string { + objectUnits(isStatic: boolean): string { // Returns a unit string corresponding to how the width for each key is specified. - if(this.formFactor == 'desktop') { + if(this.formFactor == 'desktop' || isStatic) { return '%'; } else { return 'px'; @@ -389,7 +389,7 @@ namespace com.keyman.osk { kDiv.className='kmw-key-square'; let ks=kDiv.style; - ks.width=this.objectGeometry(spec['widthpc']); + ks.width=this.objectGeometry(spec['widthpc'], osk.isStatic); let originalPercent = totalPercent; @@ -402,17 +402,19 @@ namespace com.keyman.osk { // Set key and button positioning properties. if(!isDesktop) { // Regularize interkey spacing by rounding key width and padding (Build 390) - ks.left=this.objectGeometry(totalPercent+spec['padpc']); + ks.left=this.objectGeometry(totalPercent+spec['padpc'], osk.isStatic); if(!osk.isStatic) { ks.bottom=rowStyle.bottom; } ks.height=rowStyle.height; // must be specified in px for rest of layout to work correctly - // Set distinct phone and tablet button position properties - btn.style.left=ks.left; - btn.style.width=ks.width; + if(!osk.isStatic) { + // Set distinct phone and tablet button position properties + btn.style.left=ks.left; + btn.style.width=ks.width; + } } else { - ks.marginLeft=this.objectGeometry(spec['padpc']); + ks.marginLeft=this.objectGeometry(spec['padpc'], osk.isStatic); } totalPercent=totalPercent+spec['padpc']+spec['widthpc']; @@ -461,8 +463,8 @@ namespace com.keyman.osk { return {element: kDiv, percent: totalPercent - originalPercent}; } - objectGeometry(v: number): string { - let unit = this.objectUnits(); + objectGeometry(v: number, isStatic: boolean): string { + let unit = this.objectUnits(isStatic); if(unit == '%') { return v + unit; } else { // unit == 'px' @@ -889,7 +891,7 @@ namespace com.keyman.osk { // Get the actual available document width and scale factor according to device type var objectWidth : number; - if(formFactor == 'desktop') { + if(formFactor == 'desktop' || this.isStatic) { objectWidth = 100; } else { objectWidth = oskManager.getWidth(); @@ -943,7 +945,7 @@ namespace com.keyman.osk { // Apply defaults, setting the width and other undefined properties for each key keys=row['key']; - if(!precalibrated) { + if(!precalibrated || this.isStatic) { // Calculate actual key widths by multiplying by the OSK's width and rounding appropriately, // adjusting the width of the last key to make the total exactly 100%. // Overwrite the previously-computed percent. @@ -1008,8 +1010,10 @@ namespace com.keyman.osk { lDiv.appendChild(gDiv); } - // Now that we've properly processed the keyboard's layout, mark it as calib - keyboard.markLayoutCalibrated(formFactor); + if(!this.isStatic) { + // Now that we've properly processed the keyboard's layout, mark it as calibrated. + keyboard.markLayoutCalibrated(formFactor); + } return lDiv; } //#endregion @@ -2336,7 +2340,9 @@ namespace com.keyman.osk { for(Ln=0; Ln Date: Wed, 24 Feb 2021 12:33:23 +0700 Subject: [PATCH 029/125] change(web): reverts incomplete tweak --- web/source/osk/visualKeyboard.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index eaf4f61827..09d770e38a 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -1010,10 +1010,8 @@ namespace com.keyman.osk { lDiv.appendChild(gDiv); } - if(!this.isStatic) { - // Now that we've properly processed the keyboard's layout, mark it as calibrated. - keyboard.markLayoutCalibrated(formFactor); - } + // Now that we've properly processed the keyboard's layout, mark it as calibrated. + keyboard.markLayoutCalibrated(formFactor); return lDiv; } //#endregion From 431a9fc85af42983fb20dd52403e6d6afcb00f90 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 24 Feb 2021 13:20:54 +0700 Subject: [PATCH 030/125] fix(web): hides touch-alias caret when keystroke causes focus change --- web/source/dom/domDefaultOutput.ts | 13 +++++++++++++ web/source/dom/targets/touchAlias.ts | 1 + 2 files changed, 14 insertions(+) diff --git a/web/source/dom/domDefaultOutput.ts b/web/source/dom/domDefaultOutput.ts index bf1988337a..b3471ef02c 100644 --- a/web/source/dom/domDefaultOutput.ts +++ b/web/source/dom/domDefaultOutput.ts @@ -28,14 +28,27 @@ namespace com.keyman.dom { let code = DefaultOutput.codeForEvent(Lkc); let domManager = com.keyman.singleton.domManager; + let hideCaret: () => void; + if(Lkc.Ltarg instanceof com.keyman.dom.targets.TouchAlias) { + hideCaret = function() { + let target = Lkc.Ltarg as com.keyman.dom.targets.TouchAlias; + target.root.hideCaret(); + } + } else { + hideCaret = function() {}; + } + switch(code) { case Codes.keyCodes['K_TAB']: + hideCaret(); domManager.moveToNext((Lkc.Lmodifiers & text.Codes.modifierCodes['SHIFT']) != 0); break; case Codes.keyCodes['K_TABBACK']: + hideCaret(); domManager.moveToNext(true); break; case Codes.keyCodes['K_TABFWD']: + hideCaret(); domManager.moveToNext(false); break; } diff --git a/web/source/dom/targets/touchAlias.ts b/web/source/dom/targets/touchAlias.ts index 11b68a1810..e4b75fb798 100644 --- a/web/source/dom/targets/touchAlias.ts +++ b/web/source/dom/targets/touchAlias.ts @@ -69,6 +69,7 @@ namespace com.keyman.dom.targets { this.insertTextBeforeCaret('\n'); } else if(dom.Utils.instanceof(this.root.base, "HTMLInputElement")) { // HTMLInputElements do not permit newlines; they instead have DOM-specific behaviors. + this.root.hideCaret(); Input.newlineHandler(this.root.base as HTMLInputElement); } else { console.warn("TouchAlias OutputTarget cannot output newlines for unexpected base element types!"); From 6ca508a314a1465db595e210593f96d023774e2b Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 24 Feb 2021 14:26:10 +0700 Subject: [PATCH 031/125] fix(web): removes package namespacing from kbd's CSS class --- web/source/osk/oskManager.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/source/osk/oskManager.ts b/web/source/osk/oskManager.ts index 2e56761f18..8a5c2042bc 100644 --- a/web/source/osk/oskManager.ts +++ b/web/source/osk/oskManager.ts @@ -261,8 +261,14 @@ namespace com.keyman.osk { } // Correct the classname for the (inner) OSK frame (Build 360) + var kbdID: string = (activeKeyboard ? activeKeyboard.id.replace('Keyboard_','') : ''); + if(keymanweb.isEmbedded && kbdID.indexOf('::') != -1) { + // De-namespaces the ID for use with CSS classes. + // Assumes that keyboard IDs may not contain the ':' symbol. + kbdID = kbdID.substring(kbdID.indexOf('::') + 2); + } var innerFrame= this._Box.firstChild, - kbdClass = ' kmw-keyboard-' + (activeKeyboard ? activeKeyboard.id.replace('Keyboard_','') : ''); + kbdClass = ' kmw-keyboard-' + kbdID; if(innerFrame.id == 'keymanweb_title_bar') { // Desktop order is title_bar, banner_container, inner-frame innerFrame= innerFrame.nextSibling.nextSibling; From a4e4e24cc1ff01f25126ff469a190649c9e94ead Mon Sep 17 00:00:00 2001 From: Keyman Build Agent Date: Wed, 24 Feb 2021 13:01:48 -0500 Subject: [PATCH 032/125] auto: increment beta version to 14.0.246 --- HISTORY.md | 7 +++++++ VERSION.md | 2 +- .../web/input-processor/package-lock.json | 2 +- common/core/web/input-processor/package.json | 16 +++++++-------- .../web/keyboard-processor/package-lock.json | 2 +- .../core/web/keyboard-processor/package.json | 10 +++++----- .../core/web/tools/recorder/package-lock.json | 2 +- common/core/web/tools/recorder/package.json | 10 +++++----- .../tools/sentry-manager/package-lock.json | 2 +- .../web/tools/sentry-manager/package.json | 4 ++-- common/core/web/utils/package-lock.json | 2 +- common/core/web/utils/package.json | 6 +++--- common/models/templates/package-lock.json | 2 +- common/models/templates/package.json | 8 ++++---- common/models/types/package-lock.json | 2 +- common/models/types/package.json | 2 +- common/models/wordbreakers/package-lock.json | 2 +- common/models/wordbreakers/package.json | 4 ++-- common/predictive-text/package-lock.json | 2 +- common/predictive-text/package.json | 14 ++++++------- developer/js/package-lock.json | 2 +- developer/js/package.json | 8 ++++---- lerna.json | 2 +- resources/gosh/package-lock.json | 2 +- resources/gosh/package.json | 2 +- resources/web-environment/package-lock.json | 2 +- resources/web-environment/package.json | 4 ++-- web/package-lock.json | 2 +- web/package.json | 20 +++++++++---------- 29 files changed, 76 insertions(+), 69 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index e37d70b981..c1a19cdd16 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,12 @@ # Keyman Version History +## 14.0.245 beta 2021-02-24 + +* fix(common/core/web): core key-processing now always returns RuleBehavior type. (#4508) +* fix(common/resources): Set help-keyman.com.sh executable (#4510) +* fix(developer/compilers): fixes error when "constructor" is in wordlist (#4504) +* fix(web): hides touch-alias caret when keystroke causes focus change (#4514) + ## 14.0.244 beta 2021-02-22 * fix(common/models): merges identical suggestions after casing (#4502) diff --git a/VERSION.md b/VERSION.md index ae6cc4dc1f..89cc833337 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -14.0.245 \ No newline at end of file +14.0.246 \ No newline at end of file diff --git a/common/core/web/input-processor/package-lock.json b/common/core/web/input-processor/package-lock.json index 1daec2df01..bcabc41454 100644 --- a/common/core/web/input-processor/package-lock.json +++ b/common/core/web/input-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.245", + "version": "14.0.246", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/input-processor/package.json b/common/core/web/input-processor/package.json index 4225959dda..86d9fdb58c 100644 --- a/common/core/web/input-processor/package.json +++ b/common/core/web/input-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.245", + "version": "14.0.246", "description": "The core text and prediction processing engine for KeymanWeb", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/lexical-model-compiler": "^14.0.245", - "@keymanapp/resources-gosh": "^14.0.245", + "@keymanapp/lexical-model-compiler": "^14.0.246", + "@keymanapp/resources-gosh": "^14.0.246", "@types/node": "^11.9.4", "chai": "^4.2.0", "mocha": "^5.2.0", @@ -34,11 +34,11 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.245", - "@keymanapp/lexical-model-layer": "^14.0.245", - "@keymanapp/models-types": "^14.0.245", - "@keymanapp/web-environment": "^14.0.245", - "@keymanapp/web-utils": "^14.0.245", + "@keymanapp/keyboard-processor": "^14.0.246", + "@keymanapp/lexical-model-layer": "^14.0.246", + "@keymanapp/models-types": "^14.0.246", + "@keymanapp/web-environment": "^14.0.246", + "@keymanapp/web-utils": "^14.0.246", "eventemitter3": "^4.0.0" } } diff --git a/common/core/web/keyboard-processor/package-lock.json b/common/core/web/keyboard-processor/package-lock.json index 8b76e56b1d..6d9fec46b6 100644 --- a/common/core/web/keyboard-processor/package-lock.json +++ b/common/core/web/keyboard-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.245", + "version": "14.0.246", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/keyboard-processor/package.json b/common/core/web/keyboard-processor/package.json index 86113e19b1..8d528ba666 100644 --- a/common/core/web/keyboard-processor/package.json +++ b/common/core/web/keyboard-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.245", + "version": "14.0.246", "description": "Core module for Keyman keyboard support in KeymanWeb.", "repository": { "type": "git", @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.245", + "@keymanapp/resources-gosh": "^14.0.246", "chai": "^4.2.0", "mocha": "^5.2.0", "mocha-teamcity-reporter": "^3.0.0", @@ -31,9 +31,9 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/models-types": "^14.0.245", - "@keymanapp/web-environment": "^14.0.245", - "@keymanapp/web-utils": "^14.0.245", + "@keymanapp/models-types": "^14.0.246", + "@keymanapp/web-environment": "^14.0.246", + "@keymanapp/web-utils": "^14.0.246", "@types/node": "^11.9.4", "ts-node": "^8.0.2" } diff --git a/common/core/web/tools/recorder/package-lock.json b/common/core/web/tools/recorder/package-lock.json index c0875fbf8f..ee8606ca01 100644 --- a/common/core/web/tools/recorder/package-lock.json +++ b/common/core/web/tools/recorder/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.245", + "version": "14.0.246", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/recorder/package.json b/common/core/web/tools/recorder/package.json index de46b8bc5b..7cee31d8db 100644 --- a/common/core/web/tools/recorder/package.json +++ b/common/core/web/tools/recorder/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.245", + "version": "14.0.246", "description": "Core classes used to develop KeymanWeb test cases based on keystrokes", "main": "index.js", "scripts": { @@ -17,10 +17,10 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.245", - "@keymanapp/models-types": "^14.0.245", - "@keymanapp/web-environment": "^14.0.245", - "@keymanapp/web-utils": "^14.0.245" + "@keymanapp/keyboard-processor": "^14.0.246", + "@keymanapp/models-types": "^14.0.246", + "@keymanapp/web-environment": "^14.0.246", + "@keymanapp/web-utils": "^14.0.246" }, "devDependencies": { "typescript": "^3.8.3" diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json index 249309ffc7..ff7c0bf448 100644 --- a/common/core/web/tools/sentry-manager/package-lock.json +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.245", + "version": "14.0.246", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json index f846d8718e..d8af15a542 100644 --- a/common/core/web/tools/sentry-manager/package.json +++ b/common/core/web/tools/sentry-manager/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.245", + "version": "14.0.246", "description": "Common setup for KeymanWeb's Sentry-based error reporting", "main": "dist/index.js", "scripts": { @@ -20,7 +20,7 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/web-environment": "^14.0.245", + "@keymanapp/web-environment": "^14.0.246", "@sentry/browser": "^5.27.4" } } diff --git a/common/core/web/utils/package-lock.json b/common/core/web/utils/package-lock.json index dc9ae7beef..b9bd60e6cd 100644 --- a/common/core/web/utils/package-lock.json +++ b/common/core/web/utils/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.245", + "version": "14.0.246", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/utils/package.json b/common/core/web/utils/package.json index 091fe867f9..6e3f388475 100644 --- a/common/core/web/utils/package.json +++ b/common/core/web/utils/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.245", + "version": "14.0.246", "description": "Common utility functions used throughout other Keyman packages", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -20,8 +20,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.245", - "@keymanapp/web-environment": "^14.0.245", + "@keymanapp/resources-gosh": "^14.0.246", + "@keymanapp/web-environment": "^14.0.246", "@types/node": "^14.0.5", "typescript": "^3.8.3" } diff --git a/common/models/templates/package-lock.json b/common/models/templates/package-lock.json index ca3d9a436e..e8388d55b8 100644 --- a/common/models/templates/package-lock.json +++ b/common/models/templates/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.245", + "version": "14.0.246", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/templates/package.json b/common/models/templates/package.json index 625f8fa579..90a1b21a2d 100644 --- a/common/models/templates/package.json +++ b/common/models/templates/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.245", + "version": "14.0.246", "description": "Backing model implementations (templates) for Keyman's modelling layer", "keywords": [ "lm", @@ -42,8 +42,8 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.245", - "@keymanapp/web-utils": "^14.0.245", + "@keymanapp/models-types": "^14.0.246", + "@keymanapp/web-utils": "^14.0.246", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "@types/node": "^14.0.4", @@ -52,6 +52,6 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-wordbreakers": "^14.0.245" + "@keymanapp/models-wordbreakers": "^14.0.246" } } diff --git a/common/models/types/package-lock.json b/common/models/types/package-lock.json index 9b368635b5..613121b39d 100644 --- a/common/models/types/package-lock.json +++ b/common/models/types/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.245", + "version": "14.0.246", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/types/package.json b/common/models/types/package.json index 373c988ad2..a7dd6b01d6 100644 --- a/common/models/types/package.json +++ b/common/models/types/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.245", + "version": "14.0.246", "description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.", "types": "./index.d.ts", "scripts": { diff --git a/common/models/wordbreakers/package-lock.json b/common/models/wordbreakers/package-lock.json index d257af3b48..49e682286c 100644 --- a/common/models/wordbreakers/package-lock.json +++ b/common/models/wordbreakers/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.245", + "version": "14.0.246", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json index 28ddc332fa..f52b42b7c0 100644 --- a/common/models/wordbreakers/package.json +++ b/common/models/wordbreakers/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.245", + "version": "14.0.246", "description": "Word breakers bundled in the modeling layer", "keywords": [ "lexical model", @@ -40,7 +40,7 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.245", + "@keymanapp/models-types": "^14.0.246", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "chai": "^4.2.0", diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index e70e9bb7d6..3894800d01 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.245", + "version": "14.0.246", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json index 4869f091b3..a5fef563e9 100644 --- a/common/predictive-text/package.json +++ b/common/predictive-text/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.245", + "version": "14.0.246", "description": "Keyman/Predictive Text integration layer", "main": "build/index.js", "scripts": { @@ -21,8 +21,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/models-types": "^14.0.245", - "@keymanapp/resources-gosh": "^14.0.245", + "@keymanapp/models-types": "^14.0.246", + "@keymanapp/resources-gosh": "^14.0.246", "@types/node": "^10.17.21", "chai": "^4.2.0", "karma": "^4.2.0", @@ -46,10 +46,10 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-templates": "^14.0.245", - "@keymanapp/models-wordbreakers": "^14.0.245", - "@keymanapp/web-environment": "^14.0.245", - "@keymanapp/web-utils": "^14.0.245", + "@keymanapp/models-templates": "^14.0.246", + "@keymanapp/models-wordbreakers": "^14.0.246", + "@keymanapp/web-environment": "^14.0.246", + "@keymanapp/web-utils": "^14.0.246", "es6-shim": "^0.35.5", "string.prototype.codepointat": "^0.2.1", "string.prototype.startswith": "^0.2.0" diff --git a/developer/js/package-lock.json b/developer/js/package-lock.json index b167eabd0c..d66f3bbb73 100644 --- a/developer/js/package-lock.json +++ b/developer/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.245", + "version": "14.0.246", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/developer/js/package.json b/developer/js/package.json index 593d7bd510..aab3a522a9 100644 --- a/developer/js/package.json +++ b/developer/js/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.245", + "version": "14.0.246", "description": "Keyman Developer lexical model compiler", "keywords": [ "keyboard", @@ -37,14 +37,14 @@ "node": ">=12.0.0" }, "dependencies": { - "@keymanapp/models-types": "^14.0.245", + "@keymanapp/models-types": "^14.0.246", "commander": "^3.0.0", "typescript": "^3.8.3", "xml2js": "^0.4.19" }, "devDependencies": { - "@keymanapp/models-templates": "^14.0.245", - "@keymanapp/models-wordbreakers": "^14.0.245", + "@keymanapp/models-templates": "^14.0.246", + "@keymanapp/models-wordbreakers": "^14.0.246", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", diff --git a/lerna.json b/lerna.json index 3996dd8687..99bd050f5c 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "lerna", "typescript" ], - "version": "14.0.245" + "version": "14.0.246" } diff --git a/resources/gosh/package-lock.json b/resources/gosh/package-lock.json index 66c12b8755..8560bf7b51 100644 --- a/resources/gosh/package-lock.json +++ b/resources/gosh/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.245", + "version": "14.0.246", "lockfileVersion": 1 } diff --git a/resources/gosh/package.json b/resources/gosh/package.json index 1dd0259f8c..802d37c9c6 100644 --- a/resources/gosh/package.json +++ b/resources/gosh/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.245", + "version": "14.0.246", "description": "Good Ol' Shell", "author": "Marc Durdin (https://github.com/mcdurdin)", "contributors": [ diff --git a/resources/web-environment/package-lock.json b/resources/web-environment/package-lock.json index 7fc9c7f015..dd950a52ab 100644 --- a/resources/web-environment/package-lock.json +++ b/resources/web-environment/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.245", + "version": "14.0.246", "lockfileVersion": 1 } diff --git a/resources/web-environment/package.json b/resources/web-environment/package.json index 1cc33039c8..ac7f9cbe8a 100644 --- a/resources/web-environment/package.json +++ b/resources/web-environment/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.245", + "version": "14.0.246", "description": "A compilation utility for passing select environment variables into our TS builds", "author": "Joshua Horton (https://github.com/jahorton)", "homepage": "https://github.com/keymanapp/keyman", @@ -17,6 +17,6 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.245" + "@keymanapp/resources-gosh": "^14.0.246" } } diff --git a/web/package-lock.json b/web/package-lock.json index 56b523095e..3a93ac4389 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.245", + "version": "14.0.246", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/web/package.json b/web/package.json index 559695f87a..fdaeb9daac 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.245", + "version": "14.0.246", "description": "Facilitates text input in any language.", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.245", - "@keymanapp/web-sentry-manager": "^14.0.245", + "@keymanapp/resources-gosh": "^14.0.246", + "@keymanapp/web-sentry-manager": "^14.0.246", "@sentry/cli": "^1.52.3", "chai": "^4.2.0", "google-closure-compiler-java": "^20200224.0.0", @@ -51,13 +51,13 @@ "sentry-cli": "sentry-cli" }, "dependencies": { - "@keymanapp/input-processor": "^14.0.245", - "@keymanapp/keyboard-processor": "^14.0.245", - "@keymanapp/lexical-model-layer": "^14.0.245", - "@keymanapp/models-types": "^14.0.245", - "@keymanapp/recorder-core": "^14.0.245", - "@keymanapp/web-environment": "^14.0.245", - "@keymanapp/web-utils": "^14.0.245", + "@keymanapp/input-processor": "^14.0.246", + "@keymanapp/keyboard-processor": "^14.0.246", + "@keymanapp/lexical-model-layer": "^14.0.246", + "@keymanapp/models-types": "^14.0.246", + "@keymanapp/recorder-core": "^14.0.246", + "@keymanapp/web-environment": "^14.0.246", + "@keymanapp/web-utils": "^14.0.246", "@types/node": "^11.9.4", "es6-shim": "^0.35.5", "eventemitter3": "^4.0.0", From 000867e89beda2dc783d230ddd3744367f03c53c Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 25 Feb 2021 06:28:32 +1100 Subject: [PATCH 033/125] chore(common): allow forced version increment Allows us to manually run a version increment, for example if there is a problem with a release build such as running out of space on the download server. --- resources/build/increment-version.sh | 4 ++- resources/build/version/src/fixupHistory.ts | 32 ++++++++++++++------- resources/build/version/src/index.ts | 9 ++++-- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/resources/build/increment-version.sh b/resources/build/increment-version.sh index 8e29c2116c..1d545f577b 100755 --- a/resources/build/increment-version.sh +++ b/resources/build/increment-version.sh @@ -29,9 +29,11 @@ THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BA gitbranch=`git branch --show-current` FORCE=0 +HISTORY_FORCE= if [[ "$1" == "-f" ]]; then FORCE=1 + HISTORY_FORCE=--force shift fi @@ -86,7 +88,7 @@ popd > /dev/null pushd "$KEYMAN_ROOT" > /dev/null ABORT=0 -node resources/build/version/lib/index.js history version -t "$GITHUB_TOKEN" -b "$base" || ABORT=$? +node resources/build/version/lib/index.js history version -t "$GITHUB_TOKEN" -b "$base" $HISTORY_FORCE || ABORT=$? if [[ $ABORT = 1 ]]; then if [[ $FORCE = 0 ]]; then diff --git a/resources/build/version/src/fixupHistory.ts b/resources/build/version/src/fixupHistory.ts index 00b4813d04..b6683dc56d 100644 --- a/resources/build/version/src/fixupHistory.ts +++ b/resources/build/version/src/fixupHistory.ts @@ -168,7 +168,7 @@ const splicePullsIntoHistory = async (pulls: PRInformation[]): Promise<{count: n } if(!found) { - const entry = `* ${pull.title} (#${pull.number})`; + const entry = (pull.number == 0) ? `* ${pull.title}` : `* ${pull.title} (#${pull.number})`; console.log(`-- Adding ${entry}`); historyChunks.current.splice(2, 0, entry); currentPulls.push(pull.number); @@ -246,12 +246,12 @@ export const sendCommentToPullRequestAndRelatedIssues = async ( * Adds any outstanding pull request titles to HISTORY.md for the current * version. Retrieves pull request details from GitHub. * @returns number of history entries for the current version, - * 0 if no pulls associated with the current vesrion, or + * 0 if no pulls associated with the current version, or * -1 on error. */ export const fixupHistory = async ( - octokit: GitHub, base: string + octokit: GitHub, base: string, force: boolean ): Promise => { // @@ -269,7 +269,7 @@ export const fixupHistory = async ( // const git_result = (await spawnChild('git', ['log', '--merges', /*'--first-parent',*/ '--format=%H', base, `${commit_id}..`])).trim(); - if(git_result.length == 0) { + if(git_result.length == 0 && !force) { // We won't throw on this logWarning('No pull requests found since previous increment'); return 0; @@ -284,13 +284,21 @@ export const fixupHistory = async ( let pulls: PRInformation[] = []; - for(const commit of new_commits) { - const pr = await getAssociatedPRInformation(octokit, commit); - if(pr === undefined) { - logWarning(`commit ref ${commit} has no associated pull request.`); - continue; + if(git_result.length == 0) { + pulls.push({ + title: 'No changes made', + number: 0 + }); + } + else { + for(const commit of new_commits) { + const pr = await getAssociatedPRInformation(octokit, commit); + if(pr === undefined) { + logWarning(`commit ref ${commit} has no associated pull request.`); + continue; + } + pulls.push(pr); } - pulls.push(pr); } //logInfo(JSON.stringify(pulls, null, 2)); @@ -305,7 +313,9 @@ export const fixupHistory = async ( // Write a comment to GitHub for each of the pulls // - await sendCommentToPullRequestAndRelatedIssues(octokit, historyResult.pulls); + if(git_result.length > 0) { + await sendCommentToPullRequestAndRelatedIssues(octokit, historyResult.pulls); + } return historyResult.count; }; diff --git a/resources/build/version/src/index.ts b/resources/build/version/src/index.ts index 90c53f14b5..ffbc4877b1 100644 --- a/resources/build/version/src/index.ts +++ b/resources/build/version/src/index.ts @@ -8,7 +8,7 @@ import { readFileSync } from 'fs'; const argv = yargs .command(['history'], 'Fixes up HISTORY.md with pull request data') - .command(['version'], 'Increments the current ptach version in VERSION.md') + .command(['version'], 'Increments the current patch version in VERSION.md') .demandCommand(1, 2) .options({ 'base': { @@ -22,6 +22,11 @@ const argv = yargs demandOption: true, alias: 't', type: 'string' + }, + + 'force': { + description: 'Force a version increment even if no changes found', + type: 'boolean' } }) .help() @@ -53,7 +58,7 @@ const main = async (): Promise => { if(argv._.includes('history')) { logInfo(`# Validating history for ${version}`); - changeCount = await fixupHistory(octokit, argv.base); + changeCount = await fixupHistory(octokit, argv.base, argv.force); logInfo(`# ${changeCount} change(s) found for ${version}\n`); } From dede81e2b84275ec46cabcb207f063173bc9e7dd Mon Sep 17 00:00:00 2001 From: Keyman Build Agent Date: Wed, 24 Feb 2021 14:40:17 -0500 Subject: [PATCH 034/125] auto: increment beta version to 14.0.247 --- HISTORY.md | 4 ++++ VERSION.md | 2 +- .../web/input-processor/package-lock.json | 2 +- common/core/web/input-processor/package.json | 16 +++++++-------- .../web/keyboard-processor/package-lock.json | 2 +- .../core/web/keyboard-processor/package.json | 10 +++++----- .../core/web/tools/recorder/package-lock.json | 2 +- common/core/web/tools/recorder/package.json | 10 +++++----- .../tools/sentry-manager/package-lock.json | 2 +- .../web/tools/sentry-manager/package.json | 4 ++-- common/core/web/utils/package-lock.json | 2 +- common/core/web/utils/package.json | 6 +++--- common/models/templates/package-lock.json | 2 +- common/models/templates/package.json | 8 ++++---- common/models/types/package-lock.json | 2 +- common/models/types/package.json | 2 +- common/models/wordbreakers/package-lock.json | 2 +- common/models/wordbreakers/package.json | 4 ++-- common/predictive-text/package-lock.json | 2 +- common/predictive-text/package.json | 14 ++++++------- developer/js/package-lock.json | 2 +- developer/js/package.json | 8 ++++---- lerna.json | 2 +- resources/gosh/package-lock.json | 2 +- resources/gosh/package.json | 2 +- resources/web-environment/package-lock.json | 2 +- resources/web-environment/package.json | 4 ++-- web/package-lock.json | 2 +- web/package.json | 20 +++++++++---------- 29 files changed, 73 insertions(+), 69 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index c1a19cdd16..4e41f8c8d1 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,9 @@ # Keyman Version History +## 14.0.246 beta 2021-02-24 + +* chore(common): allow forced version increment (#4522) + ## 14.0.245 beta 2021-02-24 * fix(common/core/web): core key-processing now always returns RuleBehavior type. (#4508) diff --git a/VERSION.md b/VERSION.md index 89cc833337..c4f184295d 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -14.0.246 \ No newline at end of file +14.0.247 \ No newline at end of file diff --git a/common/core/web/input-processor/package-lock.json b/common/core/web/input-processor/package-lock.json index bcabc41454..837ecb493a 100644 --- a/common/core/web/input-processor/package-lock.json +++ b/common/core/web/input-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.246", + "version": "14.0.247", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/input-processor/package.json b/common/core/web/input-processor/package.json index 86d9fdb58c..f2bd73d5b2 100644 --- a/common/core/web/input-processor/package.json +++ b/common/core/web/input-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.246", + "version": "14.0.247", "description": "The core text and prediction processing engine for KeymanWeb", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/lexical-model-compiler": "^14.0.246", - "@keymanapp/resources-gosh": "^14.0.246", + "@keymanapp/lexical-model-compiler": "^14.0.247", + "@keymanapp/resources-gosh": "^14.0.247", "@types/node": "^11.9.4", "chai": "^4.2.0", "mocha": "^5.2.0", @@ -34,11 +34,11 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.246", - "@keymanapp/lexical-model-layer": "^14.0.246", - "@keymanapp/models-types": "^14.0.246", - "@keymanapp/web-environment": "^14.0.246", - "@keymanapp/web-utils": "^14.0.246", + "@keymanapp/keyboard-processor": "^14.0.247", + "@keymanapp/lexical-model-layer": "^14.0.247", + "@keymanapp/models-types": "^14.0.247", + "@keymanapp/web-environment": "^14.0.247", + "@keymanapp/web-utils": "^14.0.247", "eventemitter3": "^4.0.0" } } diff --git a/common/core/web/keyboard-processor/package-lock.json b/common/core/web/keyboard-processor/package-lock.json index 6d9fec46b6..f95ce1e553 100644 --- a/common/core/web/keyboard-processor/package-lock.json +++ b/common/core/web/keyboard-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.246", + "version": "14.0.247", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/keyboard-processor/package.json b/common/core/web/keyboard-processor/package.json index 8d528ba666..3e5a696843 100644 --- a/common/core/web/keyboard-processor/package.json +++ b/common/core/web/keyboard-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.246", + "version": "14.0.247", "description": "Core module for Keyman keyboard support in KeymanWeb.", "repository": { "type": "git", @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.246", + "@keymanapp/resources-gosh": "^14.0.247", "chai": "^4.2.0", "mocha": "^5.2.0", "mocha-teamcity-reporter": "^3.0.0", @@ -31,9 +31,9 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/models-types": "^14.0.246", - "@keymanapp/web-environment": "^14.0.246", - "@keymanapp/web-utils": "^14.0.246", + "@keymanapp/models-types": "^14.0.247", + "@keymanapp/web-environment": "^14.0.247", + "@keymanapp/web-utils": "^14.0.247", "@types/node": "^11.9.4", "ts-node": "^8.0.2" } diff --git a/common/core/web/tools/recorder/package-lock.json b/common/core/web/tools/recorder/package-lock.json index ee8606ca01..f1c50d1527 100644 --- a/common/core/web/tools/recorder/package-lock.json +++ b/common/core/web/tools/recorder/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.246", + "version": "14.0.247", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/recorder/package.json b/common/core/web/tools/recorder/package.json index 7cee31d8db..b717a6aae0 100644 --- a/common/core/web/tools/recorder/package.json +++ b/common/core/web/tools/recorder/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.246", + "version": "14.0.247", "description": "Core classes used to develop KeymanWeb test cases based on keystrokes", "main": "index.js", "scripts": { @@ -17,10 +17,10 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.246", - "@keymanapp/models-types": "^14.0.246", - "@keymanapp/web-environment": "^14.0.246", - "@keymanapp/web-utils": "^14.0.246" + "@keymanapp/keyboard-processor": "^14.0.247", + "@keymanapp/models-types": "^14.0.247", + "@keymanapp/web-environment": "^14.0.247", + "@keymanapp/web-utils": "^14.0.247" }, "devDependencies": { "typescript": "^3.8.3" diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json index ff7c0bf448..3289446eba 100644 --- a/common/core/web/tools/sentry-manager/package-lock.json +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.246", + "version": "14.0.247", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json index d8af15a542..e8621920d7 100644 --- a/common/core/web/tools/sentry-manager/package.json +++ b/common/core/web/tools/sentry-manager/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.246", + "version": "14.0.247", "description": "Common setup for KeymanWeb's Sentry-based error reporting", "main": "dist/index.js", "scripts": { @@ -20,7 +20,7 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/web-environment": "^14.0.246", + "@keymanapp/web-environment": "^14.0.247", "@sentry/browser": "^5.27.4" } } diff --git a/common/core/web/utils/package-lock.json b/common/core/web/utils/package-lock.json index b9bd60e6cd..32a093aa21 100644 --- a/common/core/web/utils/package-lock.json +++ b/common/core/web/utils/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.246", + "version": "14.0.247", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/utils/package.json b/common/core/web/utils/package.json index 6e3f388475..1ae0bfb1fa 100644 --- a/common/core/web/utils/package.json +++ b/common/core/web/utils/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.246", + "version": "14.0.247", "description": "Common utility functions used throughout other Keyman packages", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -20,8 +20,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.246", - "@keymanapp/web-environment": "^14.0.246", + "@keymanapp/resources-gosh": "^14.0.247", + "@keymanapp/web-environment": "^14.0.247", "@types/node": "^14.0.5", "typescript": "^3.8.3" } diff --git a/common/models/templates/package-lock.json b/common/models/templates/package-lock.json index e8388d55b8..fcbc2519b1 100644 --- a/common/models/templates/package-lock.json +++ b/common/models/templates/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.246", + "version": "14.0.247", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/templates/package.json b/common/models/templates/package.json index 90a1b21a2d..04acac057e 100644 --- a/common/models/templates/package.json +++ b/common/models/templates/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.246", + "version": "14.0.247", "description": "Backing model implementations (templates) for Keyman's modelling layer", "keywords": [ "lm", @@ -42,8 +42,8 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.246", - "@keymanapp/web-utils": "^14.0.246", + "@keymanapp/models-types": "^14.0.247", + "@keymanapp/web-utils": "^14.0.247", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "@types/node": "^14.0.4", @@ -52,6 +52,6 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-wordbreakers": "^14.0.246" + "@keymanapp/models-wordbreakers": "^14.0.247" } } diff --git a/common/models/types/package-lock.json b/common/models/types/package-lock.json index 613121b39d..cf477e3b3c 100644 --- a/common/models/types/package-lock.json +++ b/common/models/types/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.246", + "version": "14.0.247", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/types/package.json b/common/models/types/package.json index a7dd6b01d6..c43118ff6c 100644 --- a/common/models/types/package.json +++ b/common/models/types/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.246", + "version": "14.0.247", "description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.", "types": "./index.d.ts", "scripts": { diff --git a/common/models/wordbreakers/package-lock.json b/common/models/wordbreakers/package-lock.json index 49e682286c..dee41bb2e0 100644 --- a/common/models/wordbreakers/package-lock.json +++ b/common/models/wordbreakers/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.246", + "version": "14.0.247", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json index f52b42b7c0..e13a0fb706 100644 --- a/common/models/wordbreakers/package.json +++ b/common/models/wordbreakers/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.246", + "version": "14.0.247", "description": "Word breakers bundled in the modeling layer", "keywords": [ "lexical model", @@ -40,7 +40,7 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.246", + "@keymanapp/models-types": "^14.0.247", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "chai": "^4.2.0", diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index 3894800d01..e74cf8802a 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.246", + "version": "14.0.247", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json index a5fef563e9..c7c042ad9a 100644 --- a/common/predictive-text/package.json +++ b/common/predictive-text/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.246", + "version": "14.0.247", "description": "Keyman/Predictive Text integration layer", "main": "build/index.js", "scripts": { @@ -21,8 +21,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/models-types": "^14.0.246", - "@keymanapp/resources-gosh": "^14.0.246", + "@keymanapp/models-types": "^14.0.247", + "@keymanapp/resources-gosh": "^14.0.247", "@types/node": "^10.17.21", "chai": "^4.2.0", "karma": "^4.2.0", @@ -46,10 +46,10 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-templates": "^14.0.246", - "@keymanapp/models-wordbreakers": "^14.0.246", - "@keymanapp/web-environment": "^14.0.246", - "@keymanapp/web-utils": "^14.0.246", + "@keymanapp/models-templates": "^14.0.247", + "@keymanapp/models-wordbreakers": "^14.0.247", + "@keymanapp/web-environment": "^14.0.247", + "@keymanapp/web-utils": "^14.0.247", "es6-shim": "^0.35.5", "string.prototype.codepointat": "^0.2.1", "string.prototype.startswith": "^0.2.0" diff --git a/developer/js/package-lock.json b/developer/js/package-lock.json index d66f3bbb73..2497b4a325 100644 --- a/developer/js/package-lock.json +++ b/developer/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.246", + "version": "14.0.247", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/developer/js/package.json b/developer/js/package.json index aab3a522a9..4e63a18d04 100644 --- a/developer/js/package.json +++ b/developer/js/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.246", + "version": "14.0.247", "description": "Keyman Developer lexical model compiler", "keywords": [ "keyboard", @@ -37,14 +37,14 @@ "node": ">=12.0.0" }, "dependencies": { - "@keymanapp/models-types": "^14.0.246", + "@keymanapp/models-types": "^14.0.247", "commander": "^3.0.0", "typescript": "^3.8.3", "xml2js": "^0.4.19" }, "devDependencies": { - "@keymanapp/models-templates": "^14.0.246", - "@keymanapp/models-wordbreakers": "^14.0.246", + "@keymanapp/models-templates": "^14.0.247", + "@keymanapp/models-wordbreakers": "^14.0.247", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", diff --git a/lerna.json b/lerna.json index 99bd050f5c..c3c7226eab 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "lerna", "typescript" ], - "version": "14.0.246" + "version": "14.0.247" } diff --git a/resources/gosh/package-lock.json b/resources/gosh/package-lock.json index 8560bf7b51..c879c9ec44 100644 --- a/resources/gosh/package-lock.json +++ b/resources/gosh/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.246", + "version": "14.0.247", "lockfileVersion": 1 } diff --git a/resources/gosh/package.json b/resources/gosh/package.json index 802d37c9c6..dc045ae918 100644 --- a/resources/gosh/package.json +++ b/resources/gosh/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.246", + "version": "14.0.247", "description": "Good Ol' Shell", "author": "Marc Durdin (https://github.com/mcdurdin)", "contributors": [ diff --git a/resources/web-environment/package-lock.json b/resources/web-environment/package-lock.json index dd950a52ab..ac80b83aa6 100644 --- a/resources/web-environment/package-lock.json +++ b/resources/web-environment/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.246", + "version": "14.0.247", "lockfileVersion": 1 } diff --git a/resources/web-environment/package.json b/resources/web-environment/package.json index ac7f9cbe8a..3583d5a9e4 100644 --- a/resources/web-environment/package.json +++ b/resources/web-environment/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.246", + "version": "14.0.247", "description": "A compilation utility for passing select environment variables into our TS builds", "author": "Joshua Horton (https://github.com/jahorton)", "homepage": "https://github.com/keymanapp/keyman", @@ -17,6 +17,6 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.246" + "@keymanapp/resources-gosh": "^14.0.247" } } diff --git a/web/package-lock.json b/web/package-lock.json index 3a93ac4389..388633ba97 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.246", + "version": "14.0.247", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/web/package.json b/web/package.json index fdaeb9daac..efb8b2a4a9 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.246", + "version": "14.0.247", "description": "Facilitates text input in any language.", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.246", - "@keymanapp/web-sentry-manager": "^14.0.246", + "@keymanapp/resources-gosh": "^14.0.247", + "@keymanapp/web-sentry-manager": "^14.0.247", "@sentry/cli": "^1.52.3", "chai": "^4.2.0", "google-closure-compiler-java": "^20200224.0.0", @@ -51,13 +51,13 @@ "sentry-cli": "sentry-cli" }, "dependencies": { - "@keymanapp/input-processor": "^14.0.246", - "@keymanapp/keyboard-processor": "^14.0.246", - "@keymanapp/lexical-model-layer": "^14.0.246", - "@keymanapp/models-types": "^14.0.246", - "@keymanapp/recorder-core": "^14.0.246", - "@keymanapp/web-environment": "^14.0.246", - "@keymanapp/web-utils": "^14.0.246", + "@keymanapp/input-processor": "^14.0.247", + "@keymanapp/keyboard-processor": "^14.0.247", + "@keymanapp/lexical-model-layer": "^14.0.247", + "@keymanapp/models-types": "^14.0.247", + "@keymanapp/recorder-core": "^14.0.247", + "@keymanapp/web-environment": "^14.0.247", + "@keymanapp/web-utils": "^14.0.247", "@types/node": "^11.9.4", "es6-shim": "^0.35.5", "eventemitter3": "^4.0.0", From 85165610816d45125ed86f24588a6f55f7ed5a95 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 25 Feb 2021 14:31:11 +0700 Subject: [PATCH 035/125] change(common/models): disables right-deletion for now --- common/predictive-text/worker/model-compositor.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/common/predictive-text/worker/model-compositor.ts b/common/predictive-text/worker/model-compositor.ts index 75e8351d91..213164a014 100644 --- a/common/predictive-text/worker/model-compositor.ts +++ b/common/predictive-text/worker/model-compositor.ts @@ -335,8 +335,18 @@ class ModelCompositor { // If we're mid-word, delete its original post-caret text. const tokenization = compositor.tokenize(context); if(tokenization && tokenization.caretSplitsToken) { - let righthandSplit = tokenization.right[0]; - suggestion.transform.deleteRight = (suggestion.transform.deleteRight || 0) + righthandSplit.kmwLength(); + // While we wait on the ability to provide a more 'ideal' solution, let's at least + // go with a more stable, if slightly less ideal, solution for now. + // + // A predictive text default (on iOS, at least) - immediately wordbreak + // on suggestions accepted mid-word. + suggestion.transform.insert += punctuation.insertAfterWord; + + // // Unfortunately, we can't quite enable the next two lines in iOS yet; there's an + // // odd issue with its textDocumentProxy that gives us the wrong results. + // + // let righthandSplit = tokenization.right[0]; + // suggestion.transform.deleteRight = (suggestion.transform.deleteRight || 0) + righthandSplit.kmwLength(); } } From fc2a17890c990bfe7241daf547e02fadded4307a Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 25 Feb 2021 14:43:35 +0700 Subject: [PATCH 036/125] docs(common/models): adds comments about bug in iOS insertText --- .../Classes/InputViewController.swift | 168 ++++++++++-------- 1 file changed, 89 insertions(+), 79 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift index 6742c7cdc7..cd4092140f 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift @@ -407,94 +407,104 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { textDocumentProxy.insertText(newText) } - // Should never change throughout the next loop; right-deletions only! - let oldLeftContext = textDocumentProxy.documentContextBeforeInput ?? "" - var pointsToDelete = numCharsToRightDelete - while pointsToDelete > 0 { - let oldContext = textDocumentProxy.documentContextAfterInput ?? "" - self.swallowContextChangeCount += 1 - // Asynchronously triggers a context change. - textDocumentProxy.adjustTextPosition(byCharacterOffset: 1) - let newContext = textDocumentProxy.documentContextAfterInput ?? "" + // NOTE: when a script uses character clusters, the code below fails to + // produce the correct effects despite `textDocumentProxy` reporting that + // the code correctly manipulates the text. There's either a bug in Apple's + // code or something critical that we're missing here. + // + // The `textDidChange` call that occurs after this `insertText` will + // report the final, 'incorrect' details, rather than what is reported + // at the end of this method. + if numCharsToRightDelete > 0 { + // Should never change throughout the next loop; right-deletions only! + let oldLeftContext = textDocumentProxy.documentContextBeforeInput ?? "" + var pointsToDelete = numCharsToRightDelete + while pointsToDelete > 0 { + let oldContext = textDocumentProxy.documentContextAfterInput ?? "" + self.swallowContextChangeCount += 1 + // Asynchronously triggers a context change. + textDocumentProxy.adjustTextPosition(byCharacterOffset: 1) + let newContext = textDocumentProxy.documentContextAfterInput ?? "" - var failsafeCount = 0 - while (textDocumentProxy.documentContextBeforeInput ?? "" != oldLeftContext) - && failsafeCount < numCharsToRightDelete { // mild breakage > locked kbd - // While adjustTextPosition makes cluster-based jumps... - // deleteBackward does not. So, we 'force' it. - textDocumentProxy.deleteBackward() - failsafeCount += 1 - } + var failsafeCount = 0 + while (textDocumentProxy.documentContextBeforeInput ?? "" != oldLeftContext) + && failsafeCount < numCharsToRightDelete { // mild breakage > locked kbd + // While adjustTextPosition makes cluster-based jumps... + // deleteBackward does not. So, we 'force' it. + textDocumentProxy.deleteBackward() + failsafeCount += 1 + } - // Determine the removed codepoint count. Also, find the index - // of the first character that shouldn't be removed, if possible. - let unitsDeleted = oldContext.utf16.count - newContext.utf16.count - var pointsDeleted = 0 + // Determine the removed codepoint count. Also, find the index + // of the first character that shouldn't be removed, if possible. + let unitsDeleted = oldContext.utf16.count - newContext.utf16.count + var pointsDeleted = 0 - // Our current index within the deleted context. - var deletedIndex = oldContext.utf16.startIndex - // Marks the first index we wish to NOT remove. - var remnantIndex: String.Index? = nil - // Marks the first index that was NOT removed by deleteBackward. - let undeletedStartIndex = oldContext.utf16.index(deletedIndex, offsetBy: unitsDeleted) + // Our current index within the deleted context. + var deletedIndex = oldContext.utf16.startIndex + // Marks the first index we wish to NOT remove. + var remnantIndex: String.Index? = nil + // Marks the first index that was NOT removed by deleteBackward. + let undeletedStartIndex = oldContext.utf16.index(deletedIndex, offsetBy: unitsDeleted) - while deletedIndex < undeletedStartIndex { - if(InputViewController.isSurrogate(oldContext.utf16[deletedIndex])) { - // Check - is it truly a surrogate pair? - let pairedIndex = oldContext.utf16.index(after: deletedIndex) - if(InputViewController.isSurrogate(oldContext.utf16[pairedIndex])) { - // If so, pre-emptively increase the index - the pair will count - // as a single character as a result. - deletedIndex = pairedIndex + while deletedIndex < undeletedStartIndex { + if(InputViewController.isSurrogate(oldContext.utf16[deletedIndex])) { + // Check - is it truly a surrogate pair? + let pairedIndex = oldContext.utf16.index(after: deletedIndex) + if(InputViewController.isSurrogate(oldContext.utf16[pairedIndex])) { + // If so, pre-emptively increase the index - the pair will count + // as a single character as a result. + deletedIndex = pairedIndex + } + } + pointsDeleted += 1 + deletedIndex = oldContext.utf16.index(after: deletedIndex) + + // Intended end of deletion found! + if pointsDeleted == pointsToDelete { + remnantIndex = deletedIndex + break // There's nothing to be gained by further loop iterations. } } - pointsDeleted += 1 - deletedIndex = oldContext.utf16.index(after: deletedIndex) - // Intended end of deletion found! - if pointsDeleted == pointsToDelete { - remnantIndex = deletedIndex - break // There's nothing to be gained by further loop iterations. + if let remnantIndex = remnantIndex, remnantIndex < undeletedStartIndex { + // We need to restore some of the deleted text! + let remnant = String(oldContext.utf16[remnantIndex.. oldLeftContext.count) { + textDocumentProxy.adjustTextPosition(byCharacterOffset: -remnant.count) + leftContext = textDocumentProxy.documentContextBeforeInput ?? "" + } + + // FAILSAFE, non-ideal behavior below. + // + // It's probably (?) better to keep the caret at the end of a cluster that + // previously started before the caret, even if right-deletions add new + // characters to the cluster. I think. + // + // Khmer example: if right-deletes leave a joeung-S that can attach + // to a main consonant to the left of my cursor, it will 'snap' into + // a cluster. It'd be more natural to have that cluster on the left, + // so I can add more chars to the cluster if desired. + // + // It's also easier to immediately backspace the extra chars if needed + // this way, rather than forcing a caret reposition. + if(leftContext.count < oldLeftContext.count) { + log.debug("Could not reposition caret perfectly after a right-delete!") + textDocumentProxy.adjustTextPosition(byCharacterOffset: 1) + } } + + // We've handled a set of code points; make sure we mark our progress! + pointsToDelete -= pointsDeleted } - - if let remnantIndex = remnantIndex, remnantIndex < undeletedStartIndex { - // We need to restore some of the deleted text! - let remnant = String(oldContext.utf16[remnantIndex.. oldLeftContext.count) { - textDocumentProxy.adjustTextPosition(byCharacterOffset: -remnant.count) - leftContext = textDocumentProxy.documentContextBeforeInput ?? "" - } - - // FAILSAFE, non-ideal behavior below. - // - // It's probably (?) better to keep the caret at the end of a cluster that - // previously started before the caret, even if right-deletions add new - // characters to the cluster. I think. - // - // Khmer example: if right-deletes leave a joeung-S that can attach - // to a main consonant to the left of my cursor, it will 'snap' into - // a cluster. It'd be more natural to have that cluster on the left, - // so I can add more chars to the cluster if desired. - // - // It's also easier to immediately backspace the extra chars if needed - // this way, rather than forcing a caret reposition. - if(leftContext.count < oldLeftContext.count) { - log.debug("Could not reposition caret perfectly after a right-delete!") - textDocumentProxy.adjustTextPosition(byCharacterOffset: 1) - } - } - - // We've handled a set of code points; make sure we mark our progress! - pointsToDelete -= pointsDeleted } } From f466604dd2db54f59d1cd73f4ca21e32f8c2da15 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Thu, 25 Feb 2021 11:43:50 +0700 Subject: [PATCH 037/125] fix(android/app): Fix welcome.htm responsiveness --- .../kmapro/KMPBrowserActivity.java | 10 ++++++++ .../tavultesoft/kmapro/PackageActivity.java | 22 +++-------------- .../tavultesoft/kmapro/WebViewFragment.java | 2 +- android/KMEA/app/src/main/AndroidManifest.xml | 17 ++++++++----- .../tavultesoft/kmea/KMHelpFileActivity.java | 24 ++++++++++++++++++- 5 files changed, 48 insertions(+), 27 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java index f25af7371a..406b9eaecc 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java @@ -13,6 +13,7 @@ import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.View; +import android.view.ViewGroup; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; @@ -162,6 +163,15 @@ public class KMPBrowserActivity extends BaseActivity { @Override protected void onDestroy() { super.onDestroy(); + // Blank the webView and destroy completely + if (webView != null) { + webView.loadUrl("about:blank"); + ((ViewGroup) webView.getParent()).removeView(webView); + webView.removeAllViews(); + webView.clearCache(true); + webView.destroy(); + webView = null; + } } @Override diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/PackageActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/PackageActivity.java index c75b49e846..95f1e94cfa 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/PackageActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/PackageActivity.java @@ -1,7 +1,9 @@ +/** + * Copyright (C) SIL International. All rights reserved. + */ package com.tavultesoft.kmapro; import android.annotation.SuppressLint; -import androidx.appcompat.widget.Toolbar; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; @@ -9,17 +11,8 @@ import androidx.fragment.app.Fragment; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.graphics.Bitmap; import android.os.Bundle; -import android.view.Gravity; import android.view.View; -import android.view.View.OnClickListener; -import android.webkit.WebChromeClient; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.Button; -import android.widget.TextView; import android.widget.Toast; @@ -36,9 +29,7 @@ import com.tavultesoft.kmea.util.KMLog; import org.json.JSONObject; import java.io.File; -import java.io.FileFilter; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -254,9 +245,6 @@ public class PackageActivity extends AppCompatActivity implements languageList = new ArrayList(); } - //Dataset kmpDataset = new Dataset(context); - //kmpDataset.keyboards.addAll(kbdsList); - List> installedPackageKeyboards = kmpProcessor.processKMP(kmpFile, tempPackagePath, PackageProcessor.PP_KEYBOARDS_KEY, languageList); // Do the notifications! @@ -270,8 +258,6 @@ public class PackageActivity extends AppCompatActivity implements Toast.LENGTH_SHORT).show(); } _cleanup = true; - notifyPackageInstallListeners(KeyboardEventHandler.EventType.PACKAGE_INSTALLED, - installedPackageKeyboards, 1); if (installedPackageKeyboards != null) { notifyPackageInstallListeners(KeyboardEventHandler.EventType.PACKAGE_INSTALLED, installedPackageKeyboards, 1); @@ -294,8 +280,6 @@ public class PackageActivity extends AppCompatActivity implements Toast.LENGTH_SHORT).show(); _cleanup = true; - notifyLexicalModelInstallListeners(KeyboardEventHandler.EventType.LEXICAL_MODEL_INSTALLED, - installedLexicalModels, 1); if (installedLexicalModels != null) { notifyLexicalModelInstallListeners(KeyboardEventHandler.EventType.LEXICAL_MODEL_INSTALLED, installedLexicalModels, 1); diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/WebViewFragment.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/WebViewFragment.java index d463f38449..8bc8a3036e 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/WebViewFragment.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/WebViewFragment.java @@ -137,7 +137,7 @@ public class WebViewFragment extends Fragment implements BlockingStep { // Inject a meta viewport tag into the head of the file if it doesn't exist webView.loadUrl( "javascript:(function() {" + - "if(!document.querySelectorAll('meta[name=viewport]').length) {"+ + "if(document.head && !document.querySelectorAll('meta[name=viewport]').length) {"+ "let meta=document.createElement('meta');"+ "meta.name='viewport';"+ "meta.content='width=device-width, initial-scale=1';"+ diff --git a/android/KMEA/app/src/main/AndroidManifest.xml b/android/KMEA/app/src/main/AndroidManifest.xml index fe795afa30..1c8428f73a 100644 --- a/android/KMEA/app/src/main/AndroidManifest.xml +++ b/android/KMEA/app/src/main/AndroidManifest.xml @@ -29,12 +29,6 @@ android:label="" android:theme="@style/Theme.AppCompat.Light.Dialog"> - - + + + + + diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java index 38e96792de..b2e3411ab3 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java @@ -13,6 +13,7 @@ import android.os.Build; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; +import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; import android.webkit.WebChromeClient; @@ -42,6 +43,7 @@ public class KMHelpFileActivity extends BaseActivity { private WebView webView; private Button finishButton; private String pkgID; + private static boolean didSetDataDirectorySuffix = false; @SuppressLint({"SetJavaScriptEnabled", "InflateParams"}) @Override @@ -49,6 +51,16 @@ public class KMHelpFileActivity extends BaseActivity { super.onCreate(savedInstanceState); final Context context = this; + // Difference processes in the same application cannot directly share WebView-related data + // https://developer.android.com/reference/android/webkit/WebView.html#setDataDirectorySuffix(java.lang.String) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + if (!didSetDataDirectorySuffix) { + String processName = getProcessName(); + WebView.setDataDirectorySuffix(processName); + didSetDataDirectorySuffix = true; + } + } + setContentView(R.layout.activity_help_file_layout); Bundle bundle = getIntent().getExtras(); @@ -132,7 +144,7 @@ public class KMHelpFileActivity extends BaseActivity { // Inject a meta viewport tag into the head of the file if it doesn't exist webView.loadUrl( "javascript:(function() {" + - "if(!document.querySelectorAll('meta[name=viewport]').length) {"+ + "if(document.head && !document.querySelectorAll('meta[name=viewport]').length) {"+ "let meta=document.createElement('meta');"+ "meta.name='viewport';"+ "meta.content='width=device-width, initial-scale=1';"+ @@ -162,6 +174,15 @@ public class KMHelpFileActivity extends BaseActivity { @Override protected void onDestroy() { super.onDestroy(); + // Blank the webView and destroy completely + if (webView != null) { + webView.loadUrl("about:blank"); + ((ViewGroup) webView.getParent()).removeView(webView); + webView.removeAllViews(); + webView.clearCache(true); + webView.destroy(); + webView = null; + } } @Override @@ -170,6 +191,7 @@ public class KMHelpFileActivity extends BaseActivity { webView.goBack(); } else { super.onBackPressed(); + finish(); } } } From 66f8ea2c3e71c7404dba7b2c8e0bfd3a6da2a586 Mon Sep 17 00:00:00 2001 From: Keyman Build Agent Date: Thu, 25 Feb 2021 13:22:15 -0500 Subject: [PATCH 038/125] auto: increment beta version to 14.0.248 --- HISTORY.md | 5 +++++ VERSION.md | 2 +- .../web/input-processor/package-lock.json | 2 +- common/core/web/input-processor/package.json | 16 +++++++-------- .../web/keyboard-processor/package-lock.json | 2 +- .../core/web/keyboard-processor/package.json | 10 +++++----- .../core/web/tools/recorder/package-lock.json | 2 +- common/core/web/tools/recorder/package.json | 10 +++++----- .../tools/sentry-manager/package-lock.json | 2 +- .../web/tools/sentry-manager/package.json | 4 ++-- common/core/web/utils/package-lock.json | 2 +- common/core/web/utils/package.json | 6 +++--- common/models/templates/package-lock.json | 2 +- common/models/templates/package.json | 8 ++++---- common/models/types/package-lock.json | 2 +- common/models/types/package.json | 2 +- common/models/wordbreakers/package-lock.json | 2 +- common/models/wordbreakers/package.json | 4 ++-- common/predictive-text/package-lock.json | 2 +- common/predictive-text/package.json | 14 ++++++------- developer/js/package-lock.json | 2 +- developer/js/package.json | 8 ++++---- lerna.json | 2 +- resources/gosh/package-lock.json | 2 +- resources/gosh/package.json | 2 +- resources/web-environment/package-lock.json | 2 +- resources/web-environment/package.json | 4 ++-- web/package-lock.json | 2 +- web/package.json | 20 +++++++++---------- 29 files changed, 74 insertions(+), 69 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 4e41f8c8d1..ca186c94bf 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,10 @@ # Keyman Version History +## 14.0.247 beta 2021-02-25 + +* fix(web): keyboard documentation patch-up (#4512) +* fix(web): removes package namespacing from kbd's CSS class (#4516) + ## 14.0.246 beta 2021-02-24 * chore(common): allow forced version increment (#4522) diff --git a/VERSION.md b/VERSION.md index c4f184295d..f325acd6d6 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -14.0.247 \ No newline at end of file +14.0.248 \ No newline at end of file diff --git a/common/core/web/input-processor/package-lock.json b/common/core/web/input-processor/package-lock.json index 837ecb493a..55f9deb699 100644 --- a/common/core/web/input-processor/package-lock.json +++ b/common/core/web/input-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.247", + "version": "14.0.248", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/input-processor/package.json b/common/core/web/input-processor/package.json index f2bd73d5b2..2696cb71af 100644 --- a/common/core/web/input-processor/package.json +++ b/common/core/web/input-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.247", + "version": "14.0.248", "description": "The core text and prediction processing engine for KeymanWeb", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/lexical-model-compiler": "^14.0.247", - "@keymanapp/resources-gosh": "^14.0.247", + "@keymanapp/lexical-model-compiler": "^14.0.248", + "@keymanapp/resources-gosh": "^14.0.248", "@types/node": "^11.9.4", "chai": "^4.2.0", "mocha": "^5.2.0", @@ -34,11 +34,11 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.247", - "@keymanapp/lexical-model-layer": "^14.0.247", - "@keymanapp/models-types": "^14.0.247", - "@keymanapp/web-environment": "^14.0.247", - "@keymanapp/web-utils": "^14.0.247", + "@keymanapp/keyboard-processor": "^14.0.248", + "@keymanapp/lexical-model-layer": "^14.0.248", + "@keymanapp/models-types": "^14.0.248", + "@keymanapp/web-environment": "^14.0.248", + "@keymanapp/web-utils": "^14.0.248", "eventemitter3": "^4.0.0" } } diff --git a/common/core/web/keyboard-processor/package-lock.json b/common/core/web/keyboard-processor/package-lock.json index f95ce1e553..283ebbd603 100644 --- a/common/core/web/keyboard-processor/package-lock.json +++ b/common/core/web/keyboard-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.247", + "version": "14.0.248", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/keyboard-processor/package.json b/common/core/web/keyboard-processor/package.json index 3e5a696843..1bfe2cf891 100644 --- a/common/core/web/keyboard-processor/package.json +++ b/common/core/web/keyboard-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.247", + "version": "14.0.248", "description": "Core module for Keyman keyboard support in KeymanWeb.", "repository": { "type": "git", @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.247", + "@keymanapp/resources-gosh": "^14.0.248", "chai": "^4.2.0", "mocha": "^5.2.0", "mocha-teamcity-reporter": "^3.0.0", @@ -31,9 +31,9 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/models-types": "^14.0.247", - "@keymanapp/web-environment": "^14.0.247", - "@keymanapp/web-utils": "^14.0.247", + "@keymanapp/models-types": "^14.0.248", + "@keymanapp/web-environment": "^14.0.248", + "@keymanapp/web-utils": "^14.0.248", "@types/node": "^11.9.4", "ts-node": "^8.0.2" } diff --git a/common/core/web/tools/recorder/package-lock.json b/common/core/web/tools/recorder/package-lock.json index f1c50d1527..b17bb2a9cb 100644 --- a/common/core/web/tools/recorder/package-lock.json +++ b/common/core/web/tools/recorder/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.247", + "version": "14.0.248", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/recorder/package.json b/common/core/web/tools/recorder/package.json index b717a6aae0..c458d51b90 100644 --- a/common/core/web/tools/recorder/package.json +++ b/common/core/web/tools/recorder/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.247", + "version": "14.0.248", "description": "Core classes used to develop KeymanWeb test cases based on keystrokes", "main": "index.js", "scripts": { @@ -17,10 +17,10 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.247", - "@keymanapp/models-types": "^14.0.247", - "@keymanapp/web-environment": "^14.0.247", - "@keymanapp/web-utils": "^14.0.247" + "@keymanapp/keyboard-processor": "^14.0.248", + "@keymanapp/models-types": "^14.0.248", + "@keymanapp/web-environment": "^14.0.248", + "@keymanapp/web-utils": "^14.0.248" }, "devDependencies": { "typescript": "^3.8.3" diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json index 3289446eba..5d57813b4b 100644 --- a/common/core/web/tools/sentry-manager/package-lock.json +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.247", + "version": "14.0.248", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json index e8621920d7..3aa4ce9326 100644 --- a/common/core/web/tools/sentry-manager/package.json +++ b/common/core/web/tools/sentry-manager/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.247", + "version": "14.0.248", "description": "Common setup for KeymanWeb's Sentry-based error reporting", "main": "dist/index.js", "scripts": { @@ -20,7 +20,7 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/web-environment": "^14.0.247", + "@keymanapp/web-environment": "^14.0.248", "@sentry/browser": "^5.27.4" } } diff --git a/common/core/web/utils/package-lock.json b/common/core/web/utils/package-lock.json index 32a093aa21..ab2a72f4b5 100644 --- a/common/core/web/utils/package-lock.json +++ b/common/core/web/utils/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.247", + "version": "14.0.248", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/utils/package.json b/common/core/web/utils/package.json index 1ae0bfb1fa..8b917a31fa 100644 --- a/common/core/web/utils/package.json +++ b/common/core/web/utils/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.247", + "version": "14.0.248", "description": "Common utility functions used throughout other Keyman packages", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -20,8 +20,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.247", - "@keymanapp/web-environment": "^14.0.247", + "@keymanapp/resources-gosh": "^14.0.248", + "@keymanapp/web-environment": "^14.0.248", "@types/node": "^14.0.5", "typescript": "^3.8.3" } diff --git a/common/models/templates/package-lock.json b/common/models/templates/package-lock.json index fcbc2519b1..4f72a0c609 100644 --- a/common/models/templates/package-lock.json +++ b/common/models/templates/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.247", + "version": "14.0.248", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/templates/package.json b/common/models/templates/package.json index 04acac057e..bfeddd7e3a 100644 --- a/common/models/templates/package.json +++ b/common/models/templates/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.247", + "version": "14.0.248", "description": "Backing model implementations (templates) for Keyman's modelling layer", "keywords": [ "lm", @@ -42,8 +42,8 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.247", - "@keymanapp/web-utils": "^14.0.247", + "@keymanapp/models-types": "^14.0.248", + "@keymanapp/web-utils": "^14.0.248", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "@types/node": "^14.0.4", @@ -52,6 +52,6 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-wordbreakers": "^14.0.247" + "@keymanapp/models-wordbreakers": "^14.0.248" } } diff --git a/common/models/types/package-lock.json b/common/models/types/package-lock.json index cf477e3b3c..364e58e00b 100644 --- a/common/models/types/package-lock.json +++ b/common/models/types/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.247", + "version": "14.0.248", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/types/package.json b/common/models/types/package.json index c43118ff6c..50b58659d0 100644 --- a/common/models/types/package.json +++ b/common/models/types/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.247", + "version": "14.0.248", "description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.", "types": "./index.d.ts", "scripts": { diff --git a/common/models/wordbreakers/package-lock.json b/common/models/wordbreakers/package-lock.json index dee41bb2e0..14cf727406 100644 --- a/common/models/wordbreakers/package-lock.json +++ b/common/models/wordbreakers/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.247", + "version": "14.0.248", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json index e13a0fb706..365259046c 100644 --- a/common/models/wordbreakers/package.json +++ b/common/models/wordbreakers/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.247", + "version": "14.0.248", "description": "Word breakers bundled in the modeling layer", "keywords": [ "lexical model", @@ -40,7 +40,7 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.247", + "@keymanapp/models-types": "^14.0.248", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "chai": "^4.2.0", diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index e74cf8802a..79fc267acc 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.247", + "version": "14.0.248", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json index c7c042ad9a..c71c3874db 100644 --- a/common/predictive-text/package.json +++ b/common/predictive-text/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.247", + "version": "14.0.248", "description": "Keyman/Predictive Text integration layer", "main": "build/index.js", "scripts": { @@ -21,8 +21,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/models-types": "^14.0.247", - "@keymanapp/resources-gosh": "^14.0.247", + "@keymanapp/models-types": "^14.0.248", + "@keymanapp/resources-gosh": "^14.0.248", "@types/node": "^10.17.21", "chai": "^4.2.0", "karma": "^4.2.0", @@ -46,10 +46,10 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-templates": "^14.0.247", - "@keymanapp/models-wordbreakers": "^14.0.247", - "@keymanapp/web-environment": "^14.0.247", - "@keymanapp/web-utils": "^14.0.247", + "@keymanapp/models-templates": "^14.0.248", + "@keymanapp/models-wordbreakers": "^14.0.248", + "@keymanapp/web-environment": "^14.0.248", + "@keymanapp/web-utils": "^14.0.248", "es6-shim": "^0.35.5", "string.prototype.codepointat": "^0.2.1", "string.prototype.startswith": "^0.2.0" diff --git a/developer/js/package-lock.json b/developer/js/package-lock.json index 2497b4a325..d0e149fe13 100644 --- a/developer/js/package-lock.json +++ b/developer/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.247", + "version": "14.0.248", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/developer/js/package.json b/developer/js/package.json index 4e63a18d04..6a352555ff 100644 --- a/developer/js/package.json +++ b/developer/js/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.247", + "version": "14.0.248", "description": "Keyman Developer lexical model compiler", "keywords": [ "keyboard", @@ -37,14 +37,14 @@ "node": ">=12.0.0" }, "dependencies": { - "@keymanapp/models-types": "^14.0.247", + "@keymanapp/models-types": "^14.0.248", "commander": "^3.0.0", "typescript": "^3.8.3", "xml2js": "^0.4.19" }, "devDependencies": { - "@keymanapp/models-templates": "^14.0.247", - "@keymanapp/models-wordbreakers": "^14.0.247", + "@keymanapp/models-templates": "^14.0.248", + "@keymanapp/models-wordbreakers": "^14.0.248", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", diff --git a/lerna.json b/lerna.json index c3c7226eab..4f54409692 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "lerna", "typescript" ], - "version": "14.0.247" + "version": "14.0.248" } diff --git a/resources/gosh/package-lock.json b/resources/gosh/package-lock.json index c879c9ec44..a1fce9060d 100644 --- a/resources/gosh/package-lock.json +++ b/resources/gosh/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.247", + "version": "14.0.248", "lockfileVersion": 1 } diff --git a/resources/gosh/package.json b/resources/gosh/package.json index dc045ae918..a28ce878d3 100644 --- a/resources/gosh/package.json +++ b/resources/gosh/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.247", + "version": "14.0.248", "description": "Good Ol' Shell", "author": "Marc Durdin (https://github.com/mcdurdin)", "contributors": [ diff --git a/resources/web-environment/package-lock.json b/resources/web-environment/package-lock.json index ac80b83aa6..b4111daa83 100644 --- a/resources/web-environment/package-lock.json +++ b/resources/web-environment/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.247", + "version": "14.0.248", "lockfileVersion": 1 } diff --git a/resources/web-environment/package.json b/resources/web-environment/package.json index 3583d5a9e4..ace696d806 100644 --- a/resources/web-environment/package.json +++ b/resources/web-environment/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.247", + "version": "14.0.248", "description": "A compilation utility for passing select environment variables into our TS builds", "author": "Joshua Horton (https://github.com/jahorton)", "homepage": "https://github.com/keymanapp/keyman", @@ -17,6 +17,6 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.247" + "@keymanapp/resources-gosh": "^14.0.248" } } diff --git a/web/package-lock.json b/web/package-lock.json index 388633ba97..4b5da063e5 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.247", + "version": "14.0.248", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/web/package.json b/web/package.json index efb8b2a4a9..4600c7cce9 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.247", + "version": "14.0.248", "description": "Facilitates text input in any language.", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.247", - "@keymanapp/web-sentry-manager": "^14.0.247", + "@keymanapp/resources-gosh": "^14.0.248", + "@keymanapp/web-sentry-manager": "^14.0.248", "@sentry/cli": "^1.52.3", "chai": "^4.2.0", "google-closure-compiler-java": "^20200224.0.0", @@ -51,13 +51,13 @@ "sentry-cli": "sentry-cli" }, "dependencies": { - "@keymanapp/input-processor": "^14.0.247", - "@keymanapp/keyboard-processor": "^14.0.247", - "@keymanapp/lexical-model-layer": "^14.0.247", - "@keymanapp/models-types": "^14.0.247", - "@keymanapp/recorder-core": "^14.0.247", - "@keymanapp/web-environment": "^14.0.247", - "@keymanapp/web-utils": "^14.0.247", + "@keymanapp/input-processor": "^14.0.248", + "@keymanapp/keyboard-processor": "^14.0.248", + "@keymanapp/lexical-model-layer": "^14.0.248", + "@keymanapp/models-types": "^14.0.248", + "@keymanapp/recorder-core": "^14.0.248", + "@keymanapp/web-environment": "^14.0.248", + "@keymanapp/web-utils": "^14.0.248", "@types/node": "^11.9.4", "es6-shim": "^0.35.5", "eventemitter3": "^4.0.0", From 27529287e2b7b41c341dc018a51b62c26b4299a9 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Fri, 26 Feb 2021 07:42:55 +1100 Subject: [PATCH 039/125] fix(windows): prevent re-registration of TIPs on 14.0 upgrade Fixes #4445. When upgrading from 11.0, 12.0 or 13.0, Keyman needs to re-register the TIPs so that we use the new registration model for 14.0. However, if we do this on a 14.0 version install, we end up registering TIPs for every single suggested language for a keyboard, which is definitely not what we want. This fix simply looks to see if we have a 14.0-style installation already, as evidenced by the presence of a `Transient Language Profiles` key, and if found, does not continue the 11to13 upgrade process. --- ...tem.UImportOlderVersionKeyboards11To13.pas | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/windows/src/desktop/kmshell/main/Keyman.Configuration.System.UImportOlderVersionKeyboards11To13.pas b/windows/src/desktop/kmshell/main/Keyman.Configuration.System.UImportOlderVersionKeyboards11To13.pas index faad0d9454..699a455fe3 100644 --- a/windows/src/desktop/kmshell/main/Keyman.Configuration.System.UImportOlderVersionKeyboards11To13.pas +++ b/windows/src/desktop/kmshell/main/Keyman.Configuration.System.UImportOlderVersionKeyboards11To13.pas @@ -4,10 +4,12 @@ interface type TImportOlderVersionKeyboards11To13 = class + private + class function ShouldRun: Boolean; + class procedure ReRegisterTips; public class procedure Execute; class procedure BackupCurrentUser; - class procedure ReRegisterTips; class procedure ImportCurrentUser; end; @@ -37,6 +39,9 @@ uses class procedure TImportOlderVersionKeyboards11To13.Execute; // I2361 begin + if not ShouldRun then + Exit; + // execute KMshell as login user to backup list of installed Keyman TIPs TUtilExecute.CreateProcessAsShellUser(ParamStr(0), '"'+ParamStr(0)+'" -upgradekeyboards=13,backup', True); @@ -101,6 +106,9 @@ var uks: TUpgradeKeyboardList; uk: TUpgradeKeyboard; begin + if not ShouldRun then + Exit; + uks := LoadUpgradeKeyboards; r := TRegistry.Create; try @@ -177,6 +185,36 @@ begin (kmcom.Keyboards as IKeymanKeyboardsInstalled2).RefreshInstalledKeyboards; end; +class function TImportOlderVersionKeyboards11To13.ShouldRun: Boolean; +var + r: TRegistry; + keys: TStringList; + i: Integer; +begin + r := TRegistry.Create(KEY_READ); + keys := TStringList.Create; + try + r.RootKey := HKEY_LOCAL_MACHINE; + if not r.OpenKeyReadOnly(SRegKey_InstalledKeyboards_LM) then + // No keyboards exist, no upgrade necessary + Exit(False); + + r.GetKeyNames(keys); + if keys.Count = 0 then + // No keyboards exist, no upgrade necessary + Exit(False); + + for i := 0 to keys.Count - 1 do + if r.KeyExists('\' + SRegKey_InstalledKeyboards_LM + '\' + keys[i] + '\' + SRegSubKey_TransientLanguageProfiles) then + // The keyboard has already been upgraded to 14.0, we mustn't upgrade again + Exit(False); + + Result := True; + finally + r.Free; + end; +end; + class procedure TImportOlderVersionKeyboards11To13.ImportCurrentUser; var r: TRegistry; @@ -191,6 +229,9 @@ var RegistrationRequired: WordBool; i: Integer; begin + if not ShouldRun then + Exit; + r := TRegistry.Create; strings := TStringList.Create; try From d7e4e1898706841d7919efa93bad9bff5e4b206b Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Fri, 26 Feb 2021 10:54:30 +1100 Subject: [PATCH 040/125] fix(windows): Handle Caps Lock event correctly from TIP Fixes #4525. This is a regression from #4468, which inadvertently added modifier checks for `VK_CAPITAL` and `VK_NUMLOCK`, when it should not have done so, as the flag was then being reset when the key was released rather than toggled. `ProcessModifierChange` pretended that it could handle `VK_CAPITAL` and `VK_NUMLOCK` but it would never have worked, and the other caller (the GetMessage hook) never passed those key events through, only Shift, Ctrl and Alt, so that's now what the TIP handler does as well. This leaves the rest of the fix from #4468 in place as that appears correct. I took the opportunity to move a repeated function declaration into keyman64.h. --- windows/src/engine/keyman32/appint/aiTIP.cpp | 4 ---- windows/src/engine/keyman32/kmhook_getmessage.cpp | 6 +----- windows/src/global/inc/keyman64.h | 1 + 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/windows/src/engine/keyman32/appint/aiTIP.cpp b/windows/src/engine/keyman32/appint/aiTIP.cpp index bb8bf1a44e..7a2b763e95 100644 --- a/windows/src/engine/keyman32/appint/aiTIP.cpp +++ b/windows/src/engine/keyman32/appint/aiTIP.cpp @@ -118,8 +118,6 @@ void ProcessToggleChange(UINT key) { // I4793 } } -void ProcessModifierChange(UINT key, BOOL isUp, BOOL isExtended); - extern "C" __declspec(dllexport) BOOL WINAPI TIPProcessKey(WPARAM wParam, LPARAM lParam, // I3589 // I3588 PKEYMANPROCESSOUTPUTFUNC outfunc, PKEYMANGETCONTEXTFUNC ctfunc, BOOL Updateable, BOOL Preserved) { @@ -174,7 +172,6 @@ extern "C" __declspec(dllexport) BOOL WINAPI TIPProcessKey(WPARAM wParam, LPARAM // in the first pass (!Updateable). KeyCapsLockPress(isUp); // I4548 } - ProcessModifierChange((UINT) wParam, isUp, extended); return FALSE; case VK_SHIFT: if (!Updateable) { @@ -190,7 +187,6 @@ extern "C" __declspec(dllexport) BOOL WINAPI TIPProcessKey(WPARAM wParam, LPARAM case VK_NUMLOCK: if(!isUp) ProcessToggleChange((UINT) wParam); // I4793 - ProcessModifierChange((UINT) wParam, isUp, extended); return FALSE; } if(isUp) { diff --git a/windows/src/engine/keyman32/kmhook_getmessage.cpp b/windows/src/engine/keyman32/kmhook_getmessage.cpp index 9d9bacb9ba..5f9593a446 100644 --- a/windows/src/engine/keyman32/kmhook_getmessage.cpp +++ b/windows/src/engine/keyman32/kmhook_getmessage.cpp @@ -109,8 +109,6 @@ LRESULT CALLBACK kmnGetMessageProc(int nCode, WPARAM wParam, LPARAM lParam) return res; } -void ProcessModifierChange(UINT key, BOOL isUp, BOOL isExtended); // I4793 - LRESULT _kmnGetMessageProc(int nCode, WPARAM wParam, LPARAM lParam) { LPMSG mp; @@ -448,7 +446,7 @@ void GetCapsAndNumlockState() { // I4793 via TSF, and we don't receive the notifications via the GetMessage hook when in UWP apps (#4369). - TODO: test whether we still need the GetMessage hook for reading modifier state. + TODO: test whether we still need the GetMessage hook for reading modifier state. */ void ProcessModifierChange(UINT key, BOOL isUp, BOOL isExtended) { // I4793 UINT flag = 0; @@ -457,8 +455,6 @@ void ProcessModifierChange(UINT key, BOOL isUp, BOOL isExtended) { // I4793 case VK_SHIFT: flag = K_SHIFTFLAG; break; case VK_MENU: flag = isExtended ? RALTFLAG : LALTFLAG; break; case VK_CONTROL: flag = isExtended ? RCTRLFLAG : LCTRLFLAG; break; - case VK_CAPITAL: flag = CAPITALFLAG; break; - case VK_NUMLOCK: flag = NUMLOCKFLAG; break; } UINT oldShiftState = Globals::get_ShiftState(); diff --git a/windows/src/global/inc/keyman64.h b/windows/src/global/inc/keyman64.h index ddd54b8c89..9b7a508991 100644 --- a/windows/src/global/inc/keyman64.h +++ b/windows/src/global/inc/keyman64.h @@ -428,6 +428,7 @@ void ReportActiveKeyboard(WORD wCommand); // I3933 // I3949 void SelectKeyboardHKL(DWORD hkl, BOOL foreground); // I3933 // I3949 // I4271 BOOL SelectKeyboardTSF(DWORD KeymanID, BOOL foreground); // I3933 // I3949 // I4271 BOOL ReportKeyboardChanged(WORD wCommand, DWORD dwProfileType, UINT langid, HKL hkl, GUID clsid, GUID guidProfile); +void ProcessModifierChange(UINT key, BOOL isUp, BOOL isExtended); // I4793 #endif From c8e6ddbe7bb1990976976f0f0a39bcb840ab42a0 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Fri, 26 Feb 2021 11:45:16 +1100 Subject: [PATCH 041/125] fix(developer): run even if sentry unavailable Fixes #4451. If sentry.dll is missing, we should continue without error reporting, rather than crashing out. --- .../general/Keyman.System.KeymanSentryClient.pas | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/windows/src/global/delphi/general/Keyman.System.KeymanSentryClient.pas b/windows/src/global/delphi/general/Keyman.System.KeymanSentryClient.pas index 095f94ce84..4af511157c 100644 --- a/windows/src/global/delphi/general/Keyman.System.KeymanSentryClient.pas +++ b/windows/src/global/delphi/general/Keyman.System.KeymanSentryClient.pas @@ -106,7 +106,13 @@ begin // Last gasp, if we are in a development situation Result := GetEnvironmentVariable(KEYMAN_ROOT); if Result <> '' then + begin Result := IncludeTrailingPathDelimiter(Result) + DEV_SENTRY_PATH; + if (Result <> '') and FileExists(Result) then + Exit; + end; + + Result := ''; end; procedure TKeymanSentryClient.ClientAfterEvent(Sender: TObject; @@ -282,7 +288,7 @@ var reg: TRegistry; o: TSentryClientOptions; f: TSentryClientFlags; - RegKey: string; + path, RegKey: string; begin Assert(not Assigned(FInstance)); @@ -293,7 +299,12 @@ begin if not Enabled then Exit; - sentry_set_library_path(FindSentryDLL); + path := FindSentryDLL; + if path = '' then + // Cannot find sentry.dll + Exit; + + sentry_set_library_path(path); o.Debug := False; From 86341505ebab684a2b8f8c137519ed483d6cd393 Mon Sep 17 00:00:00 2001 From: jahorton Date: Fri, 26 Feb 2021 09:58:27 +0700 Subject: [PATCH 042/125] fix(web): mnemonic keystrokes w FF keymapping. Fixes #1125 --- .../keyboard-processor/src/text/keyMapping.ts | 1 + web/source/dom/preProcessor.ts | 25 ++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/common/core/web/keyboard-processor/src/text/keyMapping.ts b/common/core/web/keyboard-processor/src/text/keyMapping.ts index cb45620394..4047f3c58f 100644 --- a/common/core/web/keyboard-processor/src/text/keyMapping.ts +++ b/common/core/web/keyboard-processor/src/text/keyMapping.ts @@ -18,6 +18,7 @@ namespace com.keyman { //ffie['k107'] = 187; // = // FF 3.0 // I2062 this.FF['k61'] = 187; // = // FF 2.0 this.FF['k59'] = 186; // ; + this.FF['k173'] = 189; // -/_ } } diff --git a/web/source/dom/preProcessor.ts b/web/source/dom/preProcessor.ts index e397b4bfbc..8f05072abb 100644 --- a/web/source/dom/preProcessor.ts +++ b/web/source/dom/preProcessor.ts @@ -157,6 +157,19 @@ namespace com.keyman.dom { */ s.Lmodifiers |= (e.metaKey ? modifierCodes['META']: 0); + // Physically-typed keys require use of a 'desktop' form factor and thus are based on a virtual "physical" Device. + s.device = keyman.util.physicalDevice.coreSpec; + + // Check for any browser-based keymapping before proceeding further. + // This is needed _now_ for mnenomic keyboards. (See https://github.com/keymanapp/keyman/issues/1125.) + if(!keyman.isEmbedded && s.device.browser == utils.Browser.Firefox) { + // I1466 - Convert the - keycode on mnemonic as well as positional layouts + // FireFox, Mozilla Suite + if(KeyMapping.browserMap.FF['k'+s.Lcode]) { + s.Lcode = KeyMapping.browserMap.FF['k'+s.Lcode]; + } + } + // Mnemonic handling. if(activeKeyboard && activeKeyboard.isMnemonic) { // The following will never set a code corresponding to a modifier key, so it's fine to do this, @@ -168,9 +181,6 @@ namespace com.keyman.dom { var LisVirtualKeyCode = (typeof e.charCode != 'undefined' && e.charCode != null && (e.charCode == 0 || (s.Lmodifiers & 0x6F) != 0)); s.LisVirtualKey = LisVirtualKeyCode || e.type != 'keypress'; - // Physically-typed keys require use of a 'desktop' form factor and thus are based on a virtual "physical" Device. - s.device = keyman.util.physicalDevice.coreSpec; - // This is based on a KeyboardEvent, so it's not considered 'synthetic' within web-core. s.isSynthetic = false; @@ -200,15 +210,6 @@ namespace com.keyman.dom { }; } } - - // Check for any browser-based keymapping before returning the object. - if(!keyman.isEmbedded && s.device.browser == utils.Browser.Firefox) { - // I1466 - Convert the - keycode on mnemonic as well as positional layouts - // FireFox, Mozilla Suite - if(KeyMapping.browserMap.FF['k'+s.Lcode]) { - s.Lcode = KeyMapping.browserMap.FF['k'+s.Lcode]; - } - } return s; } From e2c79b0dfac64f0817ec6af58bdee6e8f4a1aa76 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Fri, 26 Feb 2021 13:59:21 +1100 Subject: [PATCH 043/125] fix(developer): UTF-8 messages in LM compiler Fixes #4221. When the Keyman Developer IDE calls out to the lexical model compiler, it receives UTF-8 compiler message strings back but they are treated as Windows-1252 strings. This fixes that. --- .../Keyman.Developer.System.LexicalModelCompile.pas | 2 ++ 1 file changed, 2 insertions(+) diff --git a/windows/src/global/delphi/lexicalmodels/Keyman.Developer.System.LexicalModelCompile.pas b/windows/src/global/delphi/lexicalmodels/Keyman.Developer.System.LexicalModelCompile.pas index a61f8379b0..b2e88eccc1 100644 --- a/windows/src/global/delphi/lexicalmodels/Keyman.Developer.System.LexicalModelCompile.pas +++ b/windows/src/global/delphi/lexicalmodels/Keyman.Developer.System.LexicalModelCompile.pas @@ -28,6 +28,8 @@ begin cmdline := Format('"%skmlmc.cmd" "%s" -o "%s"', [ExtractFilePath(ParamStr(0)), infile, outfile]); Result := TUtilExecute.Console(cmdline, ExtractFileDir(infile), logtext, ec); + logtext := UTF8Decode(AnsiString(logtext)); + if not Result then begin ProjectFile.Project.Log(plsError, infile, From a232f32ea46c99417c21669167c549160a5d9a94 Mon Sep 17 00:00:00 2001 From: jahorton Date: Fri, 26 Feb 2021 10:32:48 +0700 Subject: [PATCH 044/125] chore: reverts right-delete changes --- .../java/com/tavultesoft/kmea/KMManager.java | 27 +-- .../worker/model-compositor.ts | 13 -- .../Classes/InputViewController.swift | 176 ++++-------------- .../Classes/KeymanWebDelegate.swift | 2 +- .../Classes/KeymanWebViewController.swift | 8 +- .../KMEI/KeymanEngine/Classes/TextView.swift | 7 + 6 files changed, 51 insertions(+), 182 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 6a0eecac0c..dce7a688ef 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -36,7 +36,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; -import android.text.Editable; import android.text.InputType; import android.util.Log; import android.view.KeyEvent; @@ -2350,6 +2349,10 @@ public final class KMManager { // This annotation is required in Jelly Bean and later: @JavascriptInterface public void insertText(final int dn, final String s, final int dr) { + if(dr != 0) { + Log.d(TAG, "Right deletions requested but are not presently supported by the in-app keyboard."); + } + Handler mainLoop = new Handler(Looper.getMainLooper()); mainLoop.post(new Runnable() { public void run() { @@ -2381,28 +2384,6 @@ public final class KMManager { start = temp; } - // As we depend upon the caret's current position for the in-app implementation, - // we need to perform right-deletions BEFORE left-deletions & text insertion. - if(dr > 0) { - for (int i = 0; i < dr; i++) { - Editable context = textView.getText(); - CharSequence chars = context.subSequence(end, context.length()); - if (chars != null && chars.length() > 0) { - char c = chars.charAt(0); - InAppKeyboardShouldIgnoreTextChange = true; - InAppKeyboardShouldIgnoreSelectionChange = true; - if (Character.isHighSurrogate(c)) { - textView.getText().delete(start, end+2); - } else { - textView.getText().delete(start, end+1); - } - - start = textView.getSelectionStart(); - end = textView.getSelectionEnd(); - } - } - } - if (dn <= 0) { if (start == end) { if (!s.isEmpty() && s.charAt(0) == '\n') { diff --git a/common/predictive-text/worker/model-compositor.ts b/common/predictive-text/worker/model-compositor.ts index 213164a014..ef03d5b706 100644 --- a/common/predictive-text/worker/model-compositor.ts +++ b/common/predictive-text/worker/model-compositor.ts @@ -341,12 +341,6 @@ class ModelCompositor { // A predictive text default (on iOS, at least) - immediately wordbreak // on suggestions accepted mid-word. suggestion.transform.insert += punctuation.insertAfterWord; - - // // Unfortunately, we can't quite enable the next two lines in iOS yet; there's an - // // odd issue with its textDocumentProxy that gives us the wrong results. - // - // let righthandSplit = tokenization.right[0]; - // suggestion.transform.deleteRight = (suggestion.transform.deleteRight || 0) + righthandSplit.kmwLength(); } } @@ -458,7 +452,6 @@ class ModelCompositor { // Step 1: generate and save the reversion's Transform. let sourceTransform = suggestion.transform; let deletedLeftChars = context.left.kmwSubstr(-sourceTransform.deleteLeft, sourceTransform.deleteLeft); - let deletedRightChars = context.right ? context.right.kmwSubstr(0, sourceTransform.deleteRight || 0) : ''; let insertedLength = sourceTransform.insert.kmwLength(); let reversionTransform: Transform = { @@ -466,18 +459,12 @@ class ModelCompositor { deleteLeft: insertedLength }; - let postCaretReversionTransform: Transform = { - insert: deletedRightChars, - deleteLeft: 0 - } - // Step 2: building the proper 'displayAs' string for the Reversion let postContext = context; if(postTransform) { // The code above restores the state to the context at the time the `Suggestion` was created. // `postTransform` handles any missing context that came later. reversionTransform = models.buildMergedTransform(reversionTransform, postTransform); - reversionTransform = models.buildMergedTransform(reversionTransform, postCaretReversionTransform); // Now that we've built the reversion based upon the Suggestion's original context, // we manipulate it in order to get a proper 'displayAs' string. diff --git a/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift index cd4092140f..35cabcb4b3 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift @@ -168,7 +168,6 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { private var keymanWeb: KeymanWebViewController private var swallowBackspaceTextChange: Bool = false - private var swallowContextChangeCount: Int = 0 open class var isPortrait: Bool { return UIScreen.main.bounds.width < UIScreen.main.bounds.height @@ -322,13 +321,6 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { self.swallowBackspaceTextChange = false return } - - // Currently motivated by the need to shift caret position to perform - // right-deletions. - if self.swallowContextChangeCount > 0 { - self.swallowContextChangeCount -= 1 - return - } let contextBeforeInput = textDocumentProxy.documentContextBeforeInput ?? "" let contextAfterInput = textDocumentProxy.documentContextAfterInput ?? "" @@ -346,7 +338,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { // We should NOT call .resetContext() here for this reason. } - func insertText(_ keymanWeb: KeymanWebViewController, numCharsToLeftDelete: Int, newText: String, numCharsToRightDelete: Int) { + func insertText(_ keymanWeb: KeymanWebViewController, numCharsToDelete: Int, newText: String) { if keymanWeb.isSubKeysMenuVisible { return } @@ -359,7 +351,9 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { perform(#selector(self.enableInputClickSound), with: nil, afterDelay: 0.1) } - if numCharsToLeftDelete <= 0 { + if numCharsToDelete <= 0 { + textDocumentProxy.insertText(newText) + // A full-context deletion will report numCharsToDelete == 0 and won't // otherwise delete selected text. if #available(iOSApplicationExtension 11.0, *) { @@ -369,143 +363,43 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { } } } - } else { - for _ in 0.. 1 { - if !InputViewController.isSurrogate(oldContext.utf16.last!) { - let lowerIndex = oldContext.utf16.index(oldContext.utf16.startIndex, - offsetBy: newContext.utf16.count) - let upperIndex = oldContext.utf16.index(lowerIndex, offsetBy: unitsDeleted - 1) - let remnant = String(oldContext.utf16[lowerIndex.. 1 { + if !InputViewController.isSurrogate(oldContext.utf16.last!) { + let lowerIndex = oldContext.utf16.index(oldContext.utf16.startIndex, + offsetBy: newContext.utf16.count) + let upperIndex = oldContext.utf16.index(lowerIndex, offsetBy: unitsDeleted - 1) + textDocumentProxy.insertText(String(oldContext[lowerIndex.. 0 { - // Should never change throughout the next loop; right-deletions only! - let oldLeftContext = textDocumentProxy.documentContextBeforeInput ?? "" - var pointsToDelete = numCharsToRightDelete - while pointsToDelete > 0 { - let oldContext = textDocumentProxy.documentContextAfterInput ?? "" - self.swallowContextChangeCount += 1 - // Asynchronously triggers a context change. - textDocumentProxy.adjustTextPosition(byCharacterOffset: 1) - let newContext = textDocumentProxy.documentContextAfterInput ?? "" - - var failsafeCount = 0 - while (textDocumentProxy.documentContextBeforeInput ?? "" != oldLeftContext) - && failsafeCount < numCharsToRightDelete { // mild breakage > locked kbd - // While adjustTextPosition makes cluster-based jumps... - // deleteBackward does not. So, we 'force' it. - textDocumentProxy.deleteBackward() - failsafeCount += 1 - } - - // Determine the removed codepoint count. Also, find the index - // of the first character that shouldn't be removed, if possible. - let unitsDeleted = oldContext.utf16.count - newContext.utf16.count - var pointsDeleted = 0 - - // Our current index within the deleted context. - var deletedIndex = oldContext.utf16.startIndex - // Marks the first index we wish to NOT remove. - var remnantIndex: String.Index? = nil - // Marks the first index that was NOT removed by deleteBackward. - let undeletedStartIndex = oldContext.utf16.index(deletedIndex, offsetBy: unitsDeleted) - - while deletedIndex < undeletedStartIndex { - if(InputViewController.isSurrogate(oldContext.utf16[deletedIndex])) { - // Check - is it truly a surrogate pair? - let pairedIndex = oldContext.utf16.index(after: deletedIndex) - if(InputViewController.isSurrogate(oldContext.utf16[pairedIndex])) { - // If so, pre-emptively increase the index - the pair will count - // as a single character as a result. - deletedIndex = pairedIndex - } - } - pointsDeleted += 1 - deletedIndex = oldContext.utf16.index(after: deletedIndex) - - // Intended end of deletion found! - if pointsDeleted == pointsToDelete { - remnantIndex = deletedIndex - break // There's nothing to be gained by further loop iterations. - } - } - - if let remnantIndex = remnantIndex, remnantIndex < undeletedStartIndex { - // We need to restore some of the deleted text! - let remnant = String(oldContext.utf16[remnantIndex.. oldLeftContext.count) { - textDocumentProxy.adjustTextPosition(byCharacterOffset: -remnant.count) - leftContext = textDocumentProxy.documentContextBeforeInput ?? "" - } - - // FAILSAFE, non-ideal behavior below. - // - // It's probably (?) better to keep the caret at the end of a cluster that - // previously started before the caret, even if right-deletions add new - // characters to the cluster. I think. - // - // Khmer example: if right-deletes leave a joeung-S that can attach - // to a main consonant to the left of my cursor, it will 'snap' into - // a cluster. It'd be more natural to have that cluster on the left, - // so I can add more chars to the cluster if desired. - // - // It's also easier to immediately backspace the extra chars if needed - // this way, rather than forcing a caret reposition. - if(leftContext.count < oldLeftContext.count) { - log.debug("Could not reposition caret perfectly after a right-delete!") - textDocumentProxy.adjustTextPosition(byCharacterOffset: 1) - } - } - - // We've handled a set of code points; make sure we mark our progress! - pointsToDelete -= pointsDeleted - } - } } func menuKeyUp(_ keymanWeb: KeymanWebViewController) { @@ -657,9 +551,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { } func resetContext() { - if self.swallowContextChangeCount <= 0 { - keymanWeb.resetContext() - } + keymanWeb.resetContext() } internal func setSentryState(enabled: Bool) { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebDelegate.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebDelegate.swift index f931b25bfa..a176a2d1ed 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebDelegate.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebDelegate.swift @@ -16,7 +16,7 @@ protocol KeymanWebDelegate: class { /// - Parameters: /// - numCharsToDelete: The number of UTF-16 code units to delete before inserting the new text. /// - newText: The string to insert. - func insertText(_ keymanWeb: KeymanWebViewController, numCharsToLeftDelete: Int, newText: String, numCharsToRightDelete: Int) + func insertText(_ keymanWeb: KeymanWebViewController, numCharsToDelete: Int, newText: String) /// - Parameters: func beep(_ keymanWeb: KeymanWebViewController) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift index 113dc6c861..e20c4f7112 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift @@ -388,14 +388,16 @@ extension KeymanWebViewController: WKScriptMessageHandler { let dn = Int(fragment[dnRange.upperBound.. Date: Fri, 26 Feb 2021 14:28:46 +0700 Subject: [PATCH 046/125] fix(ios): package installer completion requires welcome dismissal --- .../AssociatingPackageInstaller.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/AssociatingPackageInstaller.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/AssociatingPackageInstaller.swift index f83b084324..9eb8791b9a 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/AssociatingPackageInstaller.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/AssociatingPackageInstaller.swift @@ -397,11 +397,18 @@ public class AssociatingPackageInstaller(for: self.package, defaultLanguageCode: defaultLgCode, languageAssociators: associationQueriers!, pickingCompletionHandler: coreInstallationClosure(), - uiCompletionHandler: uiCompletionHandler) + uiCompletionHandler: wrappedCompletionHandler) navVC.pushViewController(pickerPrompt, animated: true) } From eabc2e4f3cd361f1c2f5233229a11e7c997642ad Mon Sep 17 00:00:00 2001 From: Keyman Build Agent Date: Fri, 26 Feb 2021 13:02:44 -0500 Subject: [PATCH 047/125] auto: increment beta version to 14.0.249 --- HISTORY.md | 12 +++++++++++ VERSION.md | 2 +- .../web/input-processor/package-lock.json | 2 +- common/core/web/input-processor/package.json | 16 +++++++-------- .../web/keyboard-processor/package-lock.json | 2 +- .../core/web/keyboard-processor/package.json | 10 +++++----- .../core/web/tools/recorder/package-lock.json | 2 +- common/core/web/tools/recorder/package.json | 10 +++++----- .../tools/sentry-manager/package-lock.json | 2 +- .../web/tools/sentry-manager/package.json | 4 ++-- common/core/web/utils/package-lock.json | 2 +- common/core/web/utils/package.json | 6 +++--- common/models/templates/package-lock.json | 2 +- common/models/templates/package.json | 8 ++++---- common/models/types/package-lock.json | 2 +- common/models/types/package.json | 2 +- common/models/wordbreakers/package-lock.json | 2 +- common/models/wordbreakers/package.json | 4 ++-- common/predictive-text/package-lock.json | 2 +- common/predictive-text/package.json | 14 ++++++------- developer/js/package-lock.json | 2 +- developer/js/package.json | 8 ++++---- lerna.json | 2 +- resources/gosh/package-lock.json | 2 +- resources/gosh/package.json | 2 +- resources/web-environment/package-lock.json | 2 +- resources/web-environment/package.json | 4 ++-- web/package-lock.json | 2 +- web/package.json | 20 +++++++++---------- 29 files changed, 81 insertions(+), 69 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index ca186c94bf..734e73bedc 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,17 @@ # Keyman Version History +## 14.0.248 beta 2021-02-26 + +* fix(common/models): predictions after context reset / caret shift (#4411) +* change(oem/fv/ios): FV keyboards now package-based (#4471) +* fix(windows): Handle Caps Lock event correctly from TIP (#4536) +* fix(developer): run even if sentry unavailable (#4537) +* fix(developer): UTF-8 messages in LM compiler (#4539) +* feat(common/models): mid-context suggestions & reversions, fix(common/models): correction-search SMP issues (#4427) +* fix(ios): package installer completion requires welcome dismissal (#4543) +* fix(windows): Refresh settings on 64-bit apps (#4378) +* fix(windows): prevent re-registration of TIPs on 14.0 upgrade (#4535) + ## 14.0.247 beta 2021-02-25 * fix(web): keyboard documentation patch-up (#4512) diff --git a/VERSION.md b/VERSION.md index f325acd6d6..41678eb31d 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -14.0.248 \ No newline at end of file +14.0.249 \ No newline at end of file diff --git a/common/core/web/input-processor/package-lock.json b/common/core/web/input-processor/package-lock.json index 55f9deb699..aa9a04616c 100644 --- a/common/core/web/input-processor/package-lock.json +++ b/common/core/web/input-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.248", + "version": "14.0.249", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/input-processor/package.json b/common/core/web/input-processor/package.json index 2696cb71af..b9e2dadcec 100644 --- a/common/core/web/input-processor/package.json +++ b/common/core/web/input-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.248", + "version": "14.0.249", "description": "The core text and prediction processing engine for KeymanWeb", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/lexical-model-compiler": "^14.0.248", - "@keymanapp/resources-gosh": "^14.0.248", + "@keymanapp/lexical-model-compiler": "^14.0.249", + "@keymanapp/resources-gosh": "^14.0.249", "@types/node": "^11.9.4", "chai": "^4.2.0", "mocha": "^5.2.0", @@ -34,11 +34,11 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.248", - "@keymanapp/lexical-model-layer": "^14.0.248", - "@keymanapp/models-types": "^14.0.248", - "@keymanapp/web-environment": "^14.0.248", - "@keymanapp/web-utils": "^14.0.248", + "@keymanapp/keyboard-processor": "^14.0.249", + "@keymanapp/lexical-model-layer": "^14.0.249", + "@keymanapp/models-types": "^14.0.249", + "@keymanapp/web-environment": "^14.0.249", + "@keymanapp/web-utils": "^14.0.249", "eventemitter3": "^4.0.0" } } diff --git a/common/core/web/keyboard-processor/package-lock.json b/common/core/web/keyboard-processor/package-lock.json index 283ebbd603..b4ea0a54af 100644 --- a/common/core/web/keyboard-processor/package-lock.json +++ b/common/core/web/keyboard-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.248", + "version": "14.0.249", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/keyboard-processor/package.json b/common/core/web/keyboard-processor/package.json index 1bfe2cf891..8fea9b6627 100644 --- a/common/core/web/keyboard-processor/package.json +++ b/common/core/web/keyboard-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.248", + "version": "14.0.249", "description": "Core module for Keyman keyboard support in KeymanWeb.", "repository": { "type": "git", @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.248", + "@keymanapp/resources-gosh": "^14.0.249", "chai": "^4.2.0", "mocha": "^5.2.0", "mocha-teamcity-reporter": "^3.0.0", @@ -31,9 +31,9 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/models-types": "^14.0.248", - "@keymanapp/web-environment": "^14.0.248", - "@keymanapp/web-utils": "^14.0.248", + "@keymanapp/models-types": "^14.0.249", + "@keymanapp/web-environment": "^14.0.249", + "@keymanapp/web-utils": "^14.0.249", "@types/node": "^11.9.4", "ts-node": "^8.0.2" } diff --git a/common/core/web/tools/recorder/package-lock.json b/common/core/web/tools/recorder/package-lock.json index b17bb2a9cb..aab15117e6 100644 --- a/common/core/web/tools/recorder/package-lock.json +++ b/common/core/web/tools/recorder/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.248", + "version": "14.0.249", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/recorder/package.json b/common/core/web/tools/recorder/package.json index c458d51b90..e6b6b8f89a 100644 --- a/common/core/web/tools/recorder/package.json +++ b/common/core/web/tools/recorder/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.248", + "version": "14.0.249", "description": "Core classes used to develop KeymanWeb test cases based on keystrokes", "main": "index.js", "scripts": { @@ -17,10 +17,10 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.248", - "@keymanapp/models-types": "^14.0.248", - "@keymanapp/web-environment": "^14.0.248", - "@keymanapp/web-utils": "^14.0.248" + "@keymanapp/keyboard-processor": "^14.0.249", + "@keymanapp/models-types": "^14.0.249", + "@keymanapp/web-environment": "^14.0.249", + "@keymanapp/web-utils": "^14.0.249" }, "devDependencies": { "typescript": "^3.8.3" diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json index 5d57813b4b..7ce4d72804 100644 --- a/common/core/web/tools/sentry-manager/package-lock.json +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.248", + "version": "14.0.249", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json index 3aa4ce9326..72c3dbfbf1 100644 --- a/common/core/web/tools/sentry-manager/package.json +++ b/common/core/web/tools/sentry-manager/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.248", + "version": "14.0.249", "description": "Common setup for KeymanWeb's Sentry-based error reporting", "main": "dist/index.js", "scripts": { @@ -20,7 +20,7 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/web-environment": "^14.0.248", + "@keymanapp/web-environment": "^14.0.249", "@sentry/browser": "^5.27.4" } } diff --git a/common/core/web/utils/package-lock.json b/common/core/web/utils/package-lock.json index ab2a72f4b5..e2a5b209b8 100644 --- a/common/core/web/utils/package-lock.json +++ b/common/core/web/utils/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.248", + "version": "14.0.249", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/utils/package.json b/common/core/web/utils/package.json index 8b917a31fa..16dcf8139e 100644 --- a/common/core/web/utils/package.json +++ b/common/core/web/utils/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.248", + "version": "14.0.249", "description": "Common utility functions used throughout other Keyman packages", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -20,8 +20,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.248", - "@keymanapp/web-environment": "^14.0.248", + "@keymanapp/resources-gosh": "^14.0.249", + "@keymanapp/web-environment": "^14.0.249", "@types/node": "^14.0.5", "typescript": "^3.8.3" } diff --git a/common/models/templates/package-lock.json b/common/models/templates/package-lock.json index 4f72a0c609..dd8ec471d1 100644 --- a/common/models/templates/package-lock.json +++ b/common/models/templates/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.248", + "version": "14.0.249", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/templates/package.json b/common/models/templates/package.json index bfeddd7e3a..193a2fb11c 100644 --- a/common/models/templates/package.json +++ b/common/models/templates/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.248", + "version": "14.0.249", "description": "Backing model implementations (templates) for Keyman's modelling layer", "keywords": [ "lm", @@ -42,8 +42,8 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.248", - "@keymanapp/web-utils": "^14.0.248", + "@keymanapp/models-types": "^14.0.249", + "@keymanapp/web-utils": "^14.0.249", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "@types/node": "^14.0.4", @@ -52,6 +52,6 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-wordbreakers": "^14.0.248" + "@keymanapp/models-wordbreakers": "^14.0.249" } } diff --git a/common/models/types/package-lock.json b/common/models/types/package-lock.json index 364e58e00b..5cca472a85 100644 --- a/common/models/types/package-lock.json +++ b/common/models/types/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.248", + "version": "14.0.249", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/types/package.json b/common/models/types/package.json index 50b58659d0..c73b07c23d 100644 --- a/common/models/types/package.json +++ b/common/models/types/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.248", + "version": "14.0.249", "description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.", "types": "./index.d.ts", "scripts": { diff --git a/common/models/wordbreakers/package-lock.json b/common/models/wordbreakers/package-lock.json index 14cf727406..ecd66b56e2 100644 --- a/common/models/wordbreakers/package-lock.json +++ b/common/models/wordbreakers/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.248", + "version": "14.0.249", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json index 365259046c..0db135b9cd 100644 --- a/common/models/wordbreakers/package.json +++ b/common/models/wordbreakers/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.248", + "version": "14.0.249", "description": "Word breakers bundled in the modeling layer", "keywords": [ "lexical model", @@ -40,7 +40,7 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.248", + "@keymanapp/models-types": "^14.0.249", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "chai": "^4.2.0", diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index 79fc267acc..8040273886 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.248", + "version": "14.0.249", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json index c71c3874db..0a0eab38bd 100644 --- a/common/predictive-text/package.json +++ b/common/predictive-text/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.248", + "version": "14.0.249", "description": "Keyman/Predictive Text integration layer", "main": "build/index.js", "scripts": { @@ -21,8 +21,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/models-types": "^14.0.248", - "@keymanapp/resources-gosh": "^14.0.248", + "@keymanapp/models-types": "^14.0.249", + "@keymanapp/resources-gosh": "^14.0.249", "@types/node": "^10.17.21", "chai": "^4.2.0", "karma": "^4.2.0", @@ -46,10 +46,10 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-templates": "^14.0.248", - "@keymanapp/models-wordbreakers": "^14.0.248", - "@keymanapp/web-environment": "^14.0.248", - "@keymanapp/web-utils": "^14.0.248", + "@keymanapp/models-templates": "^14.0.249", + "@keymanapp/models-wordbreakers": "^14.0.249", + "@keymanapp/web-environment": "^14.0.249", + "@keymanapp/web-utils": "^14.0.249", "es6-shim": "^0.35.5", "string.prototype.codepointat": "^0.2.1", "string.prototype.startswith": "^0.2.0" diff --git a/developer/js/package-lock.json b/developer/js/package-lock.json index d0e149fe13..04f2ff99ca 100644 --- a/developer/js/package-lock.json +++ b/developer/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.248", + "version": "14.0.249", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/developer/js/package.json b/developer/js/package.json index 6a352555ff..57b994aa60 100644 --- a/developer/js/package.json +++ b/developer/js/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.248", + "version": "14.0.249", "description": "Keyman Developer lexical model compiler", "keywords": [ "keyboard", @@ -37,14 +37,14 @@ "node": ">=12.0.0" }, "dependencies": { - "@keymanapp/models-types": "^14.0.248", + "@keymanapp/models-types": "^14.0.249", "commander": "^3.0.0", "typescript": "^3.8.3", "xml2js": "^0.4.19" }, "devDependencies": { - "@keymanapp/models-templates": "^14.0.248", - "@keymanapp/models-wordbreakers": "^14.0.248", + "@keymanapp/models-templates": "^14.0.249", + "@keymanapp/models-wordbreakers": "^14.0.249", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", diff --git a/lerna.json b/lerna.json index 4f54409692..1f68e76621 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "lerna", "typescript" ], - "version": "14.0.248" + "version": "14.0.249" } diff --git a/resources/gosh/package-lock.json b/resources/gosh/package-lock.json index a1fce9060d..fabe202f9f 100644 --- a/resources/gosh/package-lock.json +++ b/resources/gosh/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.248", + "version": "14.0.249", "lockfileVersion": 1 } diff --git a/resources/gosh/package.json b/resources/gosh/package.json index a28ce878d3..550caf282c 100644 --- a/resources/gosh/package.json +++ b/resources/gosh/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.248", + "version": "14.0.249", "description": "Good Ol' Shell", "author": "Marc Durdin (https://github.com/mcdurdin)", "contributors": [ diff --git a/resources/web-environment/package-lock.json b/resources/web-environment/package-lock.json index b4111daa83..c66366c27a 100644 --- a/resources/web-environment/package-lock.json +++ b/resources/web-environment/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.248", + "version": "14.0.249", "lockfileVersion": 1 } diff --git a/resources/web-environment/package.json b/resources/web-environment/package.json index ace696d806..9c7deb0dd3 100644 --- a/resources/web-environment/package.json +++ b/resources/web-environment/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.248", + "version": "14.0.249", "description": "A compilation utility for passing select environment variables into our TS builds", "author": "Joshua Horton (https://github.com/jahorton)", "homepage": "https://github.com/keymanapp/keyman", @@ -17,6 +17,6 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.248" + "@keymanapp/resources-gosh": "^14.0.249" } } diff --git a/web/package-lock.json b/web/package-lock.json index 4b5da063e5..9c5115b4be 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.248", + "version": "14.0.249", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/web/package.json b/web/package.json index 4600c7cce9..8d95311ff7 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.248", + "version": "14.0.249", "description": "Facilitates text input in any language.", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.248", - "@keymanapp/web-sentry-manager": "^14.0.248", + "@keymanapp/resources-gosh": "^14.0.249", + "@keymanapp/web-sentry-manager": "^14.0.249", "@sentry/cli": "^1.52.3", "chai": "^4.2.0", "google-closure-compiler-java": "^20200224.0.0", @@ -51,13 +51,13 @@ "sentry-cli": "sentry-cli" }, "dependencies": { - "@keymanapp/input-processor": "^14.0.248", - "@keymanapp/keyboard-processor": "^14.0.248", - "@keymanapp/lexical-model-layer": "^14.0.248", - "@keymanapp/models-types": "^14.0.248", - "@keymanapp/recorder-core": "^14.0.248", - "@keymanapp/web-environment": "^14.0.248", - "@keymanapp/web-utils": "^14.0.248", + "@keymanapp/input-processor": "^14.0.249", + "@keymanapp/keyboard-processor": "^14.0.249", + "@keymanapp/lexical-model-layer": "^14.0.249", + "@keymanapp/models-types": "^14.0.249", + "@keymanapp/recorder-core": "^14.0.249", + "@keymanapp/web-environment": "^14.0.249", + "@keymanapp/web-utils": "^14.0.249", "@types/node": "^11.9.4", "es6-shim": "^0.35.5", "eventemitter3": "^4.0.0", From 26b1d777f740dd9bfe1b109824814e2008c80829 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 1 Mar 2021 10:25:06 +1100 Subject: [PATCH 048/125] fix(developer): Improve stability of named code constants Fixes #4423. Using a named character constant with a store name that included characters outside ascii could cause kmcmpdll to crash with an assertion failure. This fix allows store names defined in the .kmn file to use characters outside ascii. Note that more work needs to be done on which characters are acceptable to use, as this is somewhat implementation-specific according to the C++ specification for `iswalpha`. Added a test case to verify various named code constant examples. Also removes dead code for `IsCJKUnifiedIdeograph` as this was never used and somewhat pointless in any case. NamedCodeConstants.cpp could stand to be rewritten using `std::map` or equivalent. Current implementation is pretty icky. --- windows/src/developer/kmcmpdll/Compiler.cpp | 11 +- .../developer/kmcmpdll/NamedCodeConstants.cpp | 357 +++++++++--------- .../developer/kmcmpdll/NamedCodeConstants.h | 12 +- windows/src/developer/kmcmpdll/kmcmpdll.h | 2 + .../src/developer/kmcmpdll/kmcmpdll.vcxproj | 6 +- windows/src/test/unit-tests/kmcomp/test.bat | 2 + .../kmcomp/test_namedcodeconstants.kmn | 23 ++ .../kmcomp/test_namedcodeconstants.txt | 1 + 8 files changed, 216 insertions(+), 198 deletions(-) create mode 100644 windows/src/test/unit-tests/kmcomp/test_namedcodeconstants.kmn create mode 100644 windows/src/test/unit-tests/kmcomp/test_namedcodeconstants.txt diff --git a/windows/src/developer/kmcmpdll/Compiler.cpp b/windows/src/developer/kmcmpdll/Compiler.cpp index 6224b35a54..1198f4f287 100644 --- a/windows/src/developer/kmcmpdll/Compiler.cpp +++ b/windows/src/developer/kmcmpdll/Compiler.cpp @@ -1051,12 +1051,10 @@ DWORD ProcessStoreLine(PFILE_KEYBOARD fk, PWSTR p) // we don't mix up named character codes which weren't supported in 5.x VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_NamedCodes); // Add a single char store as a defined character constant - char *codename = wstrtostr(sp->szName); if (Uni_IsSurrogate1(*sp->dpString)) - CodeConstants->AddCode(Uni_SurrogateToUTF32(sp->dpString[0], sp->dpString[1]), codename, fk->cxStoreArray); + CodeConstants->AddCode(Uni_SurrogateToUTF32(sp->dpString[0], sp->dpString[1]), sp->szName, fk->cxStoreArray); else - CodeConstants->AddCode(sp->dpString[0], codename, fk->cxStoreArray); - delete[] codename; + CodeConstants->AddCode(sp->dpString[0], sp->szName, fk->cxStoreArray); CodeConstants->reindex(); // has to be done after every character add due to possible use in another store. // I4982 } @@ -1908,7 +1906,6 @@ DWORD GetXString(PFILE_KEYBOARD fk, PWSTR str, PWSTR token, PWSTR output, int ma DWORD i; BOOL finished = FALSE; WCHAR c; - PSTR codename; PWCHAR tstr = NULL; int tstrMax = 0; @@ -2463,10 +2460,8 @@ DWORD GetXString(PFILE_KEYBOARD fk, PWSTR str, PWSTR token, PWSTR output, int ma q = p + 1; while (iswalnum(*q) || *q == '-' || *q == '_') q++; c = *q; *q = 0; - codename = wstrtostr(p + 1); + n = CodeConstants->GetCode(p + 1, &i); *q = c; - n = CodeConstants->GetCode(codename, &i); - delete[] codename; if (n == 0) return CERR_InvalidNamedCode; if (i < 0xFFFFFFFFL) CheckStoreUsage(fk, i, TRUE, FALSE, FALSE); // I2993 if (n > 0xFFFF) diff --git a/windows/src/developer/kmcmpdll/NamedCodeConstants.cpp b/windows/src/developer/kmcmpdll/NamedCodeConstants.cpp index 6fc9c00b41..e2923d2151 100644 --- a/windows/src/developer/kmcmpdll/NamedCodeConstants.cpp +++ b/windows/src/developer/kmcmpdll/NamedCodeConstants.cpp @@ -25,149 +25,154 @@ #include #include #include "NamedCodeConstants.h" +#include "kmcmpdll.h" extern char CompileDir[]; -int IsHangulSyllable(const char *codename, int *code); -int IsCJKUnifiedIdeograph(const char *codename, int *code); + +int IsHangulSyllable(const wchar_t *codename, int *code); BOOL FileExists(const char *filename) { - _finddata_t fi; - intptr_t n; + _finddata_t fi; + intptr_t n; - if((n = _findfirst(filename, &fi)) != -1) // I3056 // I3512 - { - _findclose(n); - return TRUE; - } - return FALSE; + if((n = _findfirst(filename, &fi)) != -1) // I3056 // I3512 + { + _findclose(n); + return TRUE; + } + return FALSE; } NamedCodeConstants::NamedCodeConstants() { - nEntries = 0; - entries = NULL; - nEntries_file = 0; - entries_file = NULL; - reindex(); + nEntries = 0; + entries = NULL; + nEntries_file = 0; + entries_file = NULL; + reindex(); } NamedCodeConstants::~NamedCodeConstants() { - if(entries) delete entries; - if(entries_file) delete entries_file; + if(entries) delete entries; + if(entries_file) delete entries_file; } -void NamedCodeConstants::AddCode(int n, char *p, DWORD storeIndex) +void NamedCodeConstants::AddCode(int n, const wchar_t *p, DWORD storeIndex) { - if((nEntries_file % ALLOC_SIZE) == 0) - { - NCCENTRY *bn = new NCCENTRY[nEntries_file + ALLOC_SIZE]; - if(nEntries_file > 0) - { - memcpy(bn, entries_file, sizeof(NCCENTRY) * nEntries_file); - delete entries_file; - } - entries_file = bn; - } + if((nEntries_file % ALLOC_SIZE) == 0) + { + NCCENTRY *bn = new NCCENTRY[nEntries_file + ALLOC_SIZE]; + if(nEntries_file > 0) + { + memcpy(bn, entries_file, sizeof(NCCENTRY) * nEntries_file); + delete entries_file; + } + entries_file = bn; + } - for(char *r = p; *r; r++) - if(!isalnum(*r) && *r != '-') *r = '_'; + entries_file[nEntries_file].code = n; + wcsncpy_s(entries_file[nEntries_file].name, _countof(entries_file[nEntries_file].name), p, MAX_ENAME); // I3481 + entries_file[nEntries_file].name[MAX_ENAME] = 0; + + for (wchar_t *r = entries_file[nEntries_file].name; *r; r++) + if (!iswalnum(*r) && *r != '-') *r = '_'; - entries_file[nEntries_file].code = n; - strncpy_s(entries_file[nEntries_file].name, _countof(entries_file[nEntries_file].name), p, MAX_ENAME); // I3481 - entries_file[nEntries_file].name[MAX_ENAME] = 0; entries_file[nEntries_file].storeIndex = storeIndex; - nEntries_file++; + nEntries_file++; } -void NamedCodeConstants::AddCode_IncludedCodes(int n, char *p) +void NamedCodeConstants::AddCode_IncludedCodes(int n, const wchar_t *p) { - if((nEntries % ALLOC_SIZE) == 0) - { - NCCENTRY *bn = new NCCENTRY[nEntries + ALLOC_SIZE]; - if(nEntries > 0) - { - memcpy(bn, entries, sizeof(NCCENTRY) * nEntries); - delete entries; - } - entries = bn; - } + if((nEntries % ALLOC_SIZE) == 0) + { + NCCENTRY *bn = new NCCENTRY[nEntries + ALLOC_SIZE]; + if(nEntries > 0) + { + memcpy(bn, entries, sizeof(NCCENTRY) * nEntries); + delete entries; + } + entries = bn; + } - for(char *r = p; *r; r++) - if(!isalnum(*r) && *r != '-') *r = '_'; + entries[nEntries].code = n; + wcsncpy_s(entries[nEntries].name, _countof(entries[nEntries].name), p, MAX_ENAME); // I3481 + entries[nEntries].name[MAX_ENAME] = 0; + for (wchar_t *r = entries[nEntries].name; *r; r++) + if (!iswalnum(*r) && *r != '-') *r = '_'; - entries[nEntries].code = n; - strncpy_s(entries[nEntries].name, _countof(entries[nEntries].name), p, MAX_ENAME); // I3481 - entries[nEntries].name[MAX_ENAME] = 0; entries[nEntries].storeIndex = 0xFFFFFFFFL; - nEntries++; + nEntries++; } int __cdecl sort_entries(const void *elem1, const void *elem2) { - return _stricmp( - ((NCCENTRY *)elem1)->name, - ((NCCENTRY *)elem2)->name); + return _wcsicmp( + ((NCCENTRY *)elem1)->name, + ((NCCENTRY *)elem2)->name); } BOOL NamedCodeConstants::IntLoadFile(const char *filename) { - FILE *fp = NULL; + FILE *fp = NULL; if(fopen_s(&fp, filename, "rt") != 0) return FALSE; // I3481 - char str[256], *p, *q, *context = NULL; - BOOL neol, first = TRUE; + char str[256], *p, *q, *context = NULL; + BOOL neol, first = TRUE; - while(fgets(str, 256, fp)) - { - neol = *(strchr(str, 0) - 1) == '\n'; - p = strtok_s(str, ";", &context); // I3481 - q = strtok_s(NULL, ";\n", &context); - if(p && q) - { + while(fgets(str, 256, fp)) + { + neol = *(strchr(str, 0) - 1) == '\n'; + p = strtok_s(str, ";", &context); // I3481 + q = strtok_s(NULL, ";\n", &context); + if(p && q) + { if(first && *p == (char)0xEF && *(p+1) == (char)0xBB && *(p+2) == (char)0xBF) p += 3; // I3056 UTF-8 // I3512 first = FALSE; - _strupr_s(q, strlen(q)+1); // I3481 // I3641 - int n = strtol(p, NULL, 16); - if(*q != '<') - AddCode_IncludedCodes(n, q); - } - if(!neol) - { - while(fgets(str, 256, fp)) if(*(strchr(str, 0)-1) == '\n') break; - } - } + _strupr_s(q, strlen(q)+1); // I3481 // I3641 + int n = strtol(p, NULL, 16); + if (*q != '<') { + PWSTR q0 = strtowstr(q); + AddCode_IncludedCodes(n, q0); + delete[] q0; + } + } + if(!neol) + { + while(fgets(str, 256, fp)) if(*(strchr(str, 0)-1) == '\n') break; + } + } - fclose(fp); + fclose(fp); - return TRUE; + return TRUE; } BOOL NamedCodeConstants::LoadFile(const char *filename) { - char buf[260]; - // Look in current directory first - strncpy_s(buf, _countof(buf), filename, 259); buf[259] = 0; // I3481 - if(FileExists(buf)) - return IntLoadFile(buf); - // Then look in keyboard file directory (CompileDir) - strncpy_s(buf, _countof(buf), CompileDir, 259); buf[259] = 0; // I3481 - strncat_s(buf, _countof(buf), filename, 259-strlen(CompileDir)); buf[259] = 0; - if(FileExists(buf)) - return IntLoadFile(buf); - // Finally look in kmcmpdll.dll directory - GetModuleFileName(0, buf, 260); - char *p = strrchr(buf, '\\'); if(p) p++; else p = buf; + char buf[260]; + // Look in current directory first + strncpy_s(buf, _countof(buf), filename, 259); buf[259] = 0; // I3481 + if(FileExists(buf)) + return IntLoadFile(buf); + // Then look in keyboard file directory (CompileDir) + strncpy_s(buf, _countof(buf), CompileDir, 259); buf[259] = 0; // I3481 + strncat_s(buf, _countof(buf), filename, 259-strlen(CompileDir)); buf[259] = 0; + if(FileExists(buf)) + return IntLoadFile(buf); + // Finally look in kmcmpdll.dll directory + GetModuleFileName(0, buf, 260); + char *p = strrchr(buf, '\\'); if(p) p++; else p = buf; *p = 0; - strncat_s(buf, _countof(buf), filename, 259-strlen(buf)); buf[259] = 0; // I3481 // I3641 - if(FileExists(buf)) - return IntLoadFile(buf); + strncat_s(buf, _countof(buf), filename, 259-strlen(buf)); buf[259] = 0; // I3481 // I3641 + if(FileExists(buf)) + return IntLoadFile(buf); - reindex(); + reindex(); - return FALSE; + return FALSE; } void NamedCodeConstants::reindex() @@ -176,50 +181,50 @@ void NamedCodeConstants::reindex() qsort(entries, nEntries, sizeof(NCCENTRY), sort_entries); } - char c = '.', d; - int i; + wchar_t c = L'.', d; + int i; - for(i = 0; i < 128; i++) chrindexes[i] = -1; + for(i = 0; i < 128; i++) chrindexes[i] = -1; if (entries != NULL) { for (i = 0; i < nEntries; i++) { - d = toupper(entries[i].name[0]); + d = towupper(entries[i].name[0]); if (d != c && d >= 32 && d <= 127) chrindexes[c = d] = i; } } } -int NamedCodeConstants::GetCode(const char *codename, DWORD *storeIndex) +int NamedCodeConstants::GetCode(const wchar_t *codename, DWORD *storeIndex) { *storeIndex = 0xFFFFFFFFL; // I2993 - int code = GetCode_IncludedCodes(codename); - if(code) return code; - for(int i = 0; i < nEntries_file; i++) - if(!_stricmp(entries_file[i].name, codename)) + int code = GetCode_IncludedCodes(codename); + if(code) return code; + for(int i = 0; i < nEntries_file; i++) + if(!_wcsicmp(entries_file[i].name, codename)) { *storeIndex = entries_file[i].storeIndex; return entries_file[i].code; } - return 0; + return 0; } -int NamedCodeConstants::GetCode_IncludedCodes(const char *codename) +int NamedCodeConstants::GetCode_IncludedCodes(const wchar_t *codename) { - char c = toupper(*codename); - int code; + wchar_t c = towupper(*codename); + int code; - if(IsHangulSyllable(codename, &code)) return code; + if(IsHangulSyllable(codename, &code)) return code; - if(c < 32 || c > 127 || chrindexes[c] < 0) return 0; - for(int n = chrindexes[c]; n < nEntries && toupper(entries[n].name[0]) == c; n++) - { - int cmp = _stricmp(codename, entries[n].name); - if(cmp == 0) return entries[n].code; - if(cmp < 0) break; - } - return 0; + if(c < 32 || c > 127 || chrindexes[c] < 0) return 0; + for(int n = chrindexes[c]; n < nEntries && towupper(entries[n].name[0]) == c; n++) + { + int cmp = _wcsicmp(codename, entries[n].name); + if(cmp == 0) return entries[n].code; + if(cmp < 0) break; + } + return 0; } /* @@ -239,92 +244,80 @@ const int HangulNCount = HangulVCount * HangulTCount, // 588 HangulSCount = HangulLCount * HangulNCount; // 11172 -const char * - Hangul_JAMO_L_TABLE[] = { - "G", "GG", "N", "D", "DD", "R", "M", "B", "BB", - "S", "SS", "", "J", "JJ", "C", "K", "T", "P", "H" }; +const wchar_t * + Hangul_JAMO_L_TABLE[] = { + L"G", L"GG", L"N", L"D", L"DD", L"R", L"M", L"B", L"BB", + L"S", L"SS", L"", L"J", L"JJ", L"C", L"K", L"T", L"P", L"H" }; -const char * - Hangul_JAMO_V_TABLE[] = { - "A", "AE", "YA", "YAE", "EO", "E", "YEO", "YE", "O", - "WA", "WAE", "OE", "YO", "U", "WEO", "WE", "WI", - "YU", "EU", "YI", "I" }; +const wchar_t * + Hangul_JAMO_V_TABLE[] = { + L"A", L"AE", L"YA", L"YAE", L"EO", L"E", L"YEO", L"YE", L"O", + L"WA", L"WAE", L"OE", L"YO", L"U", L"WEO", L"WE", L"WI", + L"YU", L"EU", L"YI", L"I" }; -const char * - Hangul_JAMO_T_TABLE[] = { - "", "G", "GG", "GS", "N", "NJ", "NH", "D", "L", "LG", "LM", - "LB", "LS", "LT", "LP", "LH", "M", "B", "BS", - "S", "SS", "NG", "J", "C", "K", "T", "P", "H" }; +const wchar_t * + Hangul_JAMO_T_TABLE[] = { + L"", L"G", L"GG", L"GS", L"N", L"NJ", L"NH", L"D", L"L", L"LG", L"LM", + L"LB", L"LS", L"LT", L"LP", L"LH", L"M", L"B", L"BS", + L"S", L"SS", L"NG", L"J", L"C", L"K", L"T", L"P", L"H" }; -int IsHangulSyllable(const char *codename, int *code) +int IsHangulSyllable(const wchar_t *codename, int *code) { - if(_strnicmp(codename, "HANGUL_SYLLABLE_", 16)) return 0; - codename += 16; - if(!*codename) return 0; + if(_wcsnicmp(codename, L"HANGUL_SYLLABLE_", 16)) return 0; + codename += 16; + if(!*codename) return 0; - int i, LIndex, VIndex, TIndex; + int i, LIndex, VIndex, TIndex; /* Find initial */ - int ch = toupper(*codename); - if(strchr("GNDRMBSJCKTPH", ch)) - { - /* Has an initial syllable */ - int fdouble = toupper(*(codename+1)) == ch; + int ch = towupper(*codename); + if(strchr("GNDRMBSJCKTPH", ch)) + { + /* Has an initial syllable */ + int fdouble = towupper(*(codename+1)) == ch; - LIndex = -1; - for(i = 0; i < HangulLCount; i++) - if(Hangul_JAMO_L_TABLE[i][0] == ch && - (!fdouble || (Hangul_JAMO_L_TABLE[i][1] == ch && fdouble))) - { - LIndex = i; - break; - } - if(LIndex == -1) return 0; - codename++; - if(fdouble) codename++; - } - else LIndex = 11; /* no initial */ + LIndex = -1; + for(i = 0; i < HangulLCount; i++) + if(Hangul_JAMO_L_TABLE[i][0] == ch && + (!fdouble || (Hangul_JAMO_L_TABLE[i][1] == ch && fdouble))) + { + LIndex = i; + break; + } + if(LIndex == -1) return 0; + codename++; + if(fdouble) codename++; + } + else LIndex = 11; /* no initial */ /* Find vowel */ - char V[4] = ""; - V[0] = *codename; - if(V[0] && strchr("AEIOUWY", toupper(*(codename+1)))) V[1] = *(codename+1); - if(V[1] && strchr("AEIOUWY", toupper(*(codename+2)))) V[2] = *(codename+2); + wchar_t V[4] = L""; + V[0] = *codename; + if(V[0] && strchr("AEIOUWY", towupper(*(codename+1)))) V[1] = *(codename+1); + if(V[1] && strchr("AEIOUWY", towupper(*(codename+2)))) V[2] = *(codename+2); - VIndex = -1; - for(i = 0; i < HangulVCount; i++) - if(!_stricmp(Hangul_JAMO_V_TABLE[i], V)) { VIndex = i; break; } + VIndex = -1; + for(i = 0; i < HangulVCount; i++) + if(!_wcsicmp(Hangul_JAMO_V_TABLE[i], V)) { VIndex = i; break; } - if(VIndex == -1) return 0; + if(VIndex == -1) return 0; - codename += strlen(V); + codename += wcslen(V); - /* Find final */ + /* Find final */ - TIndex = -1; + TIndex = -1; - for(i = 0; i < HangulTCount; i++) - if(!_stricmp(Hangul_JAMO_T_TABLE[i], codename)) { TIndex = i; break; } + for(i = 0; i < HangulTCount; i++) + if(!_wcsicmp(Hangul_JAMO_T_TABLE[i], codename)) { TIndex = i; break; } - if(TIndex == -1) return 0; + if(TIndex == -1) return 0; - /* Composition */ + /* Composition */ - *code = (HangulSBase + (LIndex * HangulVCount + VIndex) * HangulTCount) + TIndex; + *code = (HangulSBase + (LIndex * HangulVCount + VIndex) * HangulTCount) + TIndex; - return 1; -} - -int IsCJKUnifiedIdeograph(const char *codename, int *code) -{ - if(_strnicmp(codename, "CJK_UNIFIED_IDEOGRAPH", 21)) return 0; - codename += 21; - - if(strlen(codename) != 4) return 0; - - *code = strtol(codename, NULL, 16); - if(*code == 0 || *code == LONG_MAX || *code == LONG_MIN) return 0; - return 1; + return 1; } diff --git a/windows/src/developer/kmcmpdll/NamedCodeConstants.h b/windows/src/developer/kmcmpdll/NamedCodeConstants.h index e39adf7820..3efaf4ed1d 100644 --- a/windows/src/developer/kmcmpdll/NamedCodeConstants.h +++ b/windows/src/developer/kmcmpdll/NamedCodeConstants.h @@ -7,7 +7,7 @@ struct NCCENTRY { - char name[MAX_ENAME+1]; + wchar_t name[MAX_ENAME+1]; int code; DWORD storeIndex; }; @@ -20,17 +20,17 @@ private: int nEntries, nEntries_file; int chrindexes[128]; // A-Z, 0-9, -, _; simple index - int GetCode_IncludedCodes(const char *codename); - void AddCode_IncludedCodes(int n, char *p); + int GetCode_IncludedCodes(const wchar_t *codename); + void AddCode_IncludedCodes(int n, const wchar_t *p); BOOL IntLoadFile(const char *filename); public: NamedCodeConstants(); ~NamedCodeConstants(); void reindex(); - void AddCode(int n, char *p, DWORD storeIndex); + void AddCode(int n, const wchar_t *p, DWORD storeIndex); BOOL LoadFile(const char *filename); - int GetCode(const char *codename, DWORD *storeIndex); + int GetCode(const wchar_t *codename, DWORD *storeIndex); }; -#endif //_NAMEDCODECONSTANTS_H \ No newline at end of file +#endif //_NAMEDCODECONSTANTS_H diff --git a/windows/src/developer/kmcmpdll/kmcmpdll.h b/windows/src/developer/kmcmpdll/kmcmpdll.h index 88535f93e5..4e5dea8641 100644 --- a/windows/src/developer/kmcmpdll/kmcmpdll.h +++ b/windows/src/developer/kmcmpdll/kmcmpdll.h @@ -10,3 +10,5 @@ BOOL AddCompileMessage(DWORD msg); extern BOOL FWarnDeprecatedCode; extern int currentLine; + +PWSTR strtowstr(PSTR in); diff --git a/windows/src/developer/kmcmpdll/kmcmpdll.vcxproj b/windows/src/developer/kmcmpdll/kmcmpdll.vcxproj index 15d2197391..7866ee077f 100644 --- a/windows/src/developer/kmcmpdll/kmcmpdll.vcxproj +++ b/windows/src/developer/kmcmpdll/kmcmpdll.vcxproj @@ -66,11 +66,10 @@ <_ProjectFileVersion>10.0.30319.1 - $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ obj\$(Platform)\$(Configuration)\ false false - $(SolutionDir)$(Platform)\$(Configuration)\ obj\$(Platform)\$(Configuration)\ true true @@ -89,11 +88,13 @@ $(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\ext\json;..\..\ext\json-schema-validator + $(Platform)\$(Configuration)\ $(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\ext\json;..\..\ext\json-schema-validator obj\$(Platform)\$(Configuration)\ $(ProjectName).x64 + $(Platform)\$(Configuration)\ $(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\ext\json;..\..\ext\json-schema-validator @@ -102,6 +103,7 @@ $(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\ext\json;..\..\ext\json-schema-validator obj\$(Platform)\$(Configuration)\ $(ProjectName).x64 + $(Platform)\$(Configuration)\ diff --git a/windows/src/test/unit-tests/kmcomp/test.bat b/windows/src/test/unit-tests/kmcomp/test.bat index 646a950dc0..ecf6ed87c7 100644 --- a/windows/src/test/unit-tests/kmcomp/test.bat +++ b/windows/src/test/unit-tests/kmcomp/test.bat @@ -37,6 +37,8 @@ call :should-fail "#4280: if should be at start of context 2" test_4280_if_start call :should-fail "#4280: nul should be at start of context 1" test_4280_nul_start_1.kmn || goto :eof call :should-fail "#4280: nul should be at start of context 2" test_4280_nul_start_2.kmn || goto :eof +call :should-pass "#4423: named code constants, various tests" test_namedcodeconstants.kmn || goto :eof + goto :eof :should-pass diff --git a/windows/src/test/unit-tests/kmcomp/test_namedcodeconstants.kmn b/windows/src/test/unit-tests/kmcomp/test_namedcodeconstants.kmn new file mode 100644 index 0000000000..9a11c22cb0 --- /dev/null +++ b/windows/src/test/unit-tests/kmcomp/test_namedcodeconstants.kmn @@ -0,0 +1,23 @@ +c This tests #4423 -- single character stores that trigger the named character constants +c code and resulted in a codepage conversion error. + +store(&VERSION) '10.0' + +begin Unicode > use(main) + +group(main) using keys + +store(&includecodes) 'test_namedcodeconstants.txt' + +store(főő) '0' +store(bar) '1' +store(smp) U+12345 + +$főő + 'a' > 'b' +$bar + 'a' > 'c' + +$hangul_syllable_go + 'a' > 'd' + +$mycode + 'a' > 'e' + +$smp + 'a' > 'f' diff --git a/windows/src/test/unit-tests/kmcomp/test_namedcodeconstants.txt b/windows/src/test/unit-tests/kmcomp/test_namedcodeconstants.txt new file mode 100644 index 0000000000..21fe039f6b --- /dev/null +++ b/windows/src/test/unit-tests/kmcomp/test_namedcodeconstants.txt @@ -0,0 +1 @@ +0041;MYCODE;test code for 'A' From 17c94770ef0a3beca7a27b7d8b69ad2d098d1eb2 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 1 Mar 2021 10:34:30 +1100 Subject: [PATCH 049/125] chore(windows): update test project --- windows/src/test/unit-tests/kmcomp/tests.kpj | 56 +++++++++++--------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/windows/src/test/unit-tests/kmcomp/tests.kpj b/windows/src/test/unit-tests/kmcomp/tests.kpj index 7ecc383d61..14b42c913b 100644 --- a/windows/src/test/unit-tests/kmcomp/tests.kpj +++ b/windows/src/test/unit-tests/kmcomp/tests.kpj @@ -86,30 +86,6 @@ .kmn
- - id_5f9e583f7392151b3c3a7d4577b31a4f - test_valid.kmx - test_valid.kmx - - .kmx - id_03dbd77eff6b6b923de6983fab2bd9d0 - - - id_416e28ba6ecd3550e599b16a2c37e864 - test_invalid_kmx.kmx - test_invalid_kmx.kmx - - .kmx - id_98467bd680620f99f9adeb5347541b6a - - - id_ee200303f52275647138898037a80e0e - test.doc - test.doc - - .doc - id_98467bd680620f99f9adeb5347541b6a - id_4c59db88c3869d699c9df7f46731a97a test_4280_if_start_1.kmn @@ -142,5 +118,37 @@ .kmn
+ + id_5f9e583f7392151b3c3a7d4577b31a4f + test_valid.kmx + test_valid.kmx + + .kmx + id_03dbd77eff6b6b923de6983fab2bd9d0 + + + id_416e28ba6ecd3550e599b16a2c37e864 + test_invalid_kmx.kmx + test_invalid_kmx.kmx + + .kmx + id_98467bd680620f99f9adeb5347541b6a + + + id_ee200303f52275647138898037a80e0e + test.doc + test.doc + + .doc + id_98467bd680620f99f9adeb5347541b6a + + + id_068020cc7b6376ed155d5b9fc0215cc6 + test_namedcodeconstants.kmn + test_namedcodeconstants.kmn + 1.0 + .kmn +
+ From 2f2f998da65883720ccb8e041272251d3e24f73a Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 1 Mar 2021 10:57:49 +1100 Subject: [PATCH 050/125] fix(developer): schema conformance for model package compiler Fixes #3199. Ensures that the model package compiler passes schema validation for kmp.json. Three problems were corrected: 1. `followKeyboardVersion` is a source-only property; it should not be in deployable kmp.json. 2. `copyLocation` should have been a number not a string (in XML everything is a string... but JSON has more control here) 3. `fileType` is a source-only property; it also should not be in deployable kmp.json. I have checked that the resulting files validate against the package kmp.json schema version 1.1.0 defined at https://github.com/keymanapp/api.keyman.com/blob/master/schemas/package/1.1.0/package.json. None of these fixes should impact the use of these packages in target apps. `followKeyboardVersion` and `fileType` are used only in the source at package development time, and `copyLocation` is unused (and should be eliminated from Keyman altogether in the future). --- developer/js/build.sh | 11 +++++---- .../source/package-compiler/kmp-compiler.ts | 23 +++++++++++-------- .../source/package-compiler/kmp-json-file.ts | 1 - .../example.qaa.sencoten.model.kmp.json | 7 ++---- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/developer/js/build.sh b/developer/js/build.sh index b1547cf52c..3f67c493dc 100755 --- a/developer/js/build.sh +++ b/developer/js/build.sh @@ -27,11 +27,12 @@ display_usage ( ) { echo "Usage: $0 [-test] [-publish-to-npm]" echo " $0 -help" echo - echo " -help displays this screen and exits" - echo " -test runs unit tests after building" - echo " -tdd runs unit tests WITHOUT building" - echo " -publish-to-npm publishes the current version to the npm package index" - echo " -dry-run do build, etc, but don't actually publish" + echo " -help displays this screen and exits" + echo " -test runs unit tests after building" + echo " -tdd runs unit tests WITHOUT building" + echo " -skip-package-install, -S skip package installation" + echo " -publish-to-npm publishes the current version to the npm package index" + echo " -dry-run do build, etc, but don't actually publish" } ################################ Main script ################################ diff --git a/developer/js/source/package-compiler/kmp-compiler.ts b/developer/js/source/package-compiler/kmp-compiler.ts index 8538bf8c19..5a16738397 100644 --- a/developer/js/source/package-compiler/kmp-compiler.ts +++ b/developer/js/source/package-compiler/kmp-compiler.ts @@ -32,8 +32,9 @@ export default class KmpCompiler { // 1. Unwrap arrays (and convert to array where single object) // 2. Fix casing on `iD` // 3. Rewrap info, keyboard.languages, lexicalModel.languages, startMenu.items elements - // 4. Convert options.followKeyboardVersion to a bool - // 5. Filenames need to be basenames (but this comes after processing) + // 4. Remove options.followKeyboardVersion, file.fileType + // 5. Convert file.copyLocation to a Number + // 6. Filenames need to be basenames (but this comes after processing) // // Helper functions @@ -66,9 +67,7 @@ export default class KmpCompiler { let kmp: KmpJsonFile = { system: kps.system, - options: { - followKeyboardVersion: kps.options.followKeyboardVersion === '' - } + options: {} }; // Fill in additional fields @@ -76,10 +75,7 @@ export default class KmpCompiler { let keys: (keyof KpsFileOptions & keyof KmpJsonFileOptions)[] = ['executeProgram', 'graphicFile', 'msiFilename', 'msiOptions', 'readmeFile']; for (let element of keys) { if (kps.options[element]) { - // TypeScript thinks it's possible to assign to followKeyboardVersion (a - // boolean), but in reality, all of the other keys are strings. - // Politely inform TypeScript that it's okay to do this assignment: - ( kmp.options[element]) = kps.options[element]; + kmp.options[element] = kps.options[element]; } } @@ -88,7 +84,14 @@ export default class KmpCompiler { } if(kps.files && kps.files.file) { - kmp.files = arrayWrap(kps.files.file); + kmp.files = arrayWrap(kps.files.file).map((file: KpsFileContentFile) => { + return { + name: file.name, + description: file.description, + copyLocation: parseInt(file.copyLocation, 10) + // note: we don't emit fileType as that is not permitted in kmp.json + } + }); } if(kps.keyboards && kps.keyboards.keyboard) { diff --git a/developer/js/source/package-compiler/kmp-json-file.ts b/developer/js/source/package-compiler/kmp-json-file.ts index da47567dfb..3eadb3dd1d 100644 --- a/developer/js/source/package-compiler/kmp-json-file.ts +++ b/developer/js/source/package-compiler/kmp-json-file.ts @@ -15,7 +15,6 @@ interface KmpJsonFileSystem { } interface KmpJsonFileOptions { - followKeyboardVersion: boolean; readmeFile?: string; graphicFile?: string; executeProgram?: string; diff --git a/developer/js/tests/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kmp.json b/developer/js/tests/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kmp.json index 903b1d9023..b7215a3610 100644 --- a/developer/js/tests/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kmp.json +++ b/developer/js/tests/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kmp.json @@ -3,9 +3,7 @@ "keymanDeveloperVersion": "12.0.1500.0", "fileVersion": "12.0" }, - "options": { - "followKeyboardVersion": true - }, + "options": {}, "info": { "author": { "description": "Eddie Antonio Santos", @@ -25,8 +23,7 @@ { "name": "..\\build\\example.qaa.sencoten.model.js", "description": "Lexical model example.qaa.sencoten.model.js", - "copyLocation": "0", - "fileType": ".model.js" + "copyLocation": 0 } ], "lexicalModels": [ From 9ce0d722671a164db53aa28d28e2b8d410d14966 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Mon, 1 Mar 2021 10:02:10 +0700 Subject: [PATCH 051/125] chore(ios/app): Adjust help titles for installing custom dictionaries --- ios/help/basic/index.md | 2 +- ...oards.md => installing-custom-keyboards-dictionaries.md} | 6 +++--- ios/help/basic/sharing-keyboards.md | 2 +- ios/help/index.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) rename ios/help/basic/{installing-custom-keyboards.md => installing-custom-keyboards-dictionaries.md} (90%) diff --git a/ios/help/basic/index.md b/ios/help/basic/index.md index 0a5cbcc11d..491e7d952c 100644 --- a/ios/help/basic/index.md +++ b/ios/help/basic/index.md @@ -4,7 +4,7 @@ title: Basic Help * [Switching Between Keyboards](switching-between-keyboards) -* [Installing Custom Keyboards](installing-custom-keyboards) +* [Installing Custom Keyboards/Dictionaries](installing-custom-keyboards-dictionaries) * [Sharing Keyboards](sharing-keyboards) * [Using the Keyman Browser](using-keyman-browser) diff --git a/ios/help/basic/installing-custom-keyboards.md b/ios/help/basic/installing-custom-keyboards-dictionaries.md similarity index 90% rename from ios/help/basic/installing-custom-keyboards.md rename to ios/help/basic/installing-custom-keyboards-dictionaries.md index e56a240cb6..7bd9d66cad 100644 --- a/ios/help/basic/installing-custom-keyboards.md +++ b/ios/help/basic/installing-custom-keyboards-dictionaries.md @@ -1,10 +1,10 @@ --- -title: Installing Custom Keyboards - Keyman for iPhone and iPad Help +title: Installing Custom Keyboards/Dictionaries - Keyman for iPhone and iPad Help --- -The following steps can also be used to install a dictionary package. +The following steps can be used to install either a keyboard package or a dictionary package. -###Download the File +### Download the File If downloading a custom keyboard from the internet, click the link to your custom keyboard package file. For this example, we'll install a custom keyboard from a link in Safari. Our example keyboard is for the GFF Amharic 7 keyboard. diff --git a/ios/help/basic/sharing-keyboards.md b/ios/help/basic/sharing-keyboards.md index 8cd217ab55..1fc07a9068 100644 --- a/ios/help/basic/sharing-keyboards.md +++ b/ios/help/basic/sharing-keyboards.md @@ -45,4 +45,4 @@ Selecting the notification seen at the top will lead you to the following page: Select the big **Install on iPhone** (or **Install on iPad**) option will then download -the file for easy installation. (Refer to [Installing custom keyboards](installing-custom-keyboards) as necessary.) +the file for easy installation. (Refer to [Installing custom keyboards/dictionaries](installing-custom-keyboards-dictionaries) as necessary.) diff --git a/ios/help/index.md b/ios/help/index.md index 8602639a08..eba2b1bf3b 100644 --- a/ios/help/index.md +++ b/ios/help/index.md @@ -24,7 +24,7 @@ title: Keyman for iPhone and iPad 14.0 Help ### [Using Keyman for iPhone and iPad](basic/) * [Switching Between Keyboards](basic/switching-between-keyboards) -* [Installing Custom Keyboards](basic/installing-custom-keyboards) +* [Installing Custom Keyboards/Dictionaries](basic/installing-custom-keyboards-dictionaries) * [Sharing Keyboards](basic/sharing-keyboards) * [Using the Keyman Browser](basic/using-keyman-browser) * [Removing Keyboards](basic/uninstalling-keyboards) From 3f54c8146152f3b1bc04d2f45076c3c60a257daa Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 1 Mar 2021 15:44:55 +1100 Subject: [PATCH 052/125] fix(developer): touch layout osk import handling of multiple modifiers Fixes #2740. The definitions for several of the combined modifiers was incorrect, which led to a mismatch when attempting to import those layers from a visual keyboard into the touch layout editor. --- .../TIKE/oskbuilder/TouchLayoutDefinitions.pas | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/windows/src/developer/TIKE/oskbuilder/TouchLayoutDefinitions.pas b/windows/src/developer/TIKE/oskbuilder/TouchLayoutDefinitions.pas index 0a55570d06..29d08b6098 100644 --- a/windows/src/developer/TIKE/oskbuilder/TouchLayoutDefinitions.pas +++ b/windows/src/developer/TIKE/oskbuilder/TouchLayoutDefinitions.pas @@ -123,12 +123,12 @@ const 'rightalt', 'rightctrl-rightalt', //'shift', - 'shift-lctrl', - 'shift-rctrl', - 'shift-lalt', - 'shift-ralt', - 'shift-lctrl-lalt', - 'shift-rctrl-ralt' + 'leftctrl-shift', + 'rightctrl-shift', + 'leftalt-shift', + 'rightalt-shift', + 'leftctrl-leftalt-shift', + 'rightctrl-rightalt-shift' ); implementation From 1fcfefa3cf8b3e9a2f91c387e43c851cb018a031 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 1 Mar 2021 16:05:17 +1100 Subject: [PATCH 053/125] fix(developer): support for smp names --- windows/src/developer/kmcmpdll/Compiler.cpp | 2 +- windows/src/developer/kmcmpdll/NamedCodeConstants.cpp | 4 ++-- .../src/test/unit-tests/kmcomp/test_namedcodeconstants.kmn | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/windows/src/developer/kmcmpdll/Compiler.cpp b/windows/src/developer/kmcmpdll/Compiler.cpp index 1198f4f287..444c1126ef 100644 --- a/windows/src/developer/kmcmpdll/Compiler.cpp +++ b/windows/src/developer/kmcmpdll/Compiler.cpp @@ -2458,7 +2458,7 @@ DWORD GetXString(PFILE_KEYBOARD fk, PWSTR str, PWSTR token, PWSTR output, int ma case 16: VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_NamedCodes); q = p + 1; - while (iswalnum(*q) || *q == '-' || *q == '_') q++; + while (*q && !iswblank(*q)) q++; c = *q; *q = 0; n = CodeConstants->GetCode(p + 1, &i); *q = c; diff --git a/windows/src/developer/kmcmpdll/NamedCodeConstants.cpp b/windows/src/developer/kmcmpdll/NamedCodeConstants.cpp index e2923d2151..a676230a6e 100644 --- a/windows/src/developer/kmcmpdll/NamedCodeConstants.cpp +++ b/windows/src/developer/kmcmpdll/NamedCodeConstants.cpp @@ -78,7 +78,7 @@ void NamedCodeConstants::AddCode(int n, const wchar_t *p, DWORD storeIndex) entries_file[nEntries_file].name[MAX_ENAME] = 0; for (wchar_t *r = entries_file[nEntries_file].name; *r; r++) - if (!iswalnum(*r) && *r != '-') *r = '_'; + if (iswblank(*r) && *r != '-') *r = '_'; entries_file[nEntries_file].storeIndex = storeIndex; nEntries_file++; @@ -101,7 +101,7 @@ void NamedCodeConstants::AddCode_IncludedCodes(int n, const wchar_t *p) wcsncpy_s(entries[nEntries].name, _countof(entries[nEntries].name), p, MAX_ENAME); // I3481 entries[nEntries].name[MAX_ENAME] = 0; for (wchar_t *r = entries[nEntries].name; *r; r++) - if (!iswalnum(*r) && *r != '-') *r = '_'; + if (iswblank(*r)) *r = '_'; entries[nEntries].storeIndex = 0xFFFFFFFFL; nEntries++; diff --git a/windows/src/test/unit-tests/kmcomp/test_namedcodeconstants.kmn b/windows/src/test/unit-tests/kmcomp/test_namedcodeconstants.kmn index 9a11c22cb0..0c696448c3 100644 --- a/windows/src/test/unit-tests/kmcomp/test_namedcodeconstants.kmn +++ b/windows/src/test/unit-tests/kmcomp/test_namedcodeconstants.kmn @@ -12,6 +12,7 @@ store(&includecodes) 'test_namedcodeconstants.txt' store(főő) '0' store(bar) '1' store(smp) U+12345 +store(😉) '🤪' $főő + 'a' > 'b' $bar + 'a' > 'c' @@ -21,3 +22,4 @@ $hangul_syllable_go + 'a' > 'd' $mycode + 'a' > 'e' $smp + 'a' > 'f' +$😉 + 'a' > '😡' From 3c4b783691d68bdc0ab58f08eea6e2ee6537f1f8 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Mon, 1 Mar 2021 13:27:41 +0700 Subject: [PATCH 054/125] fix(android/engine): Destroy keyboard to free webViews when viewing help --- .../src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java index b2e3411ab3..60ed2f4664 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java @@ -51,6 +51,9 @@ public class KMHelpFileActivity extends BaseActivity { super.onCreate(savedInstanceState); final Context context = this; + // Teardown keyboards to free up webView + KMManager.onDestroy(); + // Difference processes in the same application cannot directly share WebView-related data // https://developer.android.com/reference/android/webkit/WebView.html#setDataDirectorySuffix(java.lang.String) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { From c0b103d65d2204c9cee146c1e20ac0a3b1a3c409 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Mon, 1 Mar 2021 14:31:46 +0700 Subject: [PATCH 055/125] Apply suggestions from code review Co-authored-by: Joshua Horton --- .../src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java index 60ed2f4664..5ed8bae829 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java @@ -54,7 +54,7 @@ public class KMHelpFileActivity extends BaseActivity { // Teardown keyboards to free up webView KMManager.onDestroy(); - // Difference processes in the same application cannot directly share WebView-related data + // Different processes in the same application cannot directly share WebView-related data // https://developer.android.com/reference/android/webkit/WebView.html#setDataDirectorySuffix(java.lang.String) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (!didSetDataDirectorySuffix) { From 6ba69a04ce7c24c4ca6a1357975237f2460b3ec5 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Mon, 1 Mar 2021 15:25:46 +0700 Subject: [PATCH 056/125] change(android/engine): Refactor viewport injection --- .../tavultesoft/kmapro/WebViewFragment.java | 12 ++-------- .../tavultesoft/kmea/KMHelpFileActivity.java | 12 ++-------- .../tavultesoft/kmea/util/WebViewUtil.java | 24 +++++++++++++++++++ 3 files changed, 28 insertions(+), 20 deletions(-) create mode 100644 android/KMEA/app/src/main/java/com/tavultesoft/kmea/util/WebViewUtil.java diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/WebViewFragment.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/WebViewFragment.java index 8bc8a3036e..6ea360d9c2 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/WebViewFragment.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/WebViewFragment.java @@ -14,6 +14,7 @@ import com.stepstone.stepper.StepperLayout; import com.stepstone.stepper.VerificationError; import com.tavultesoft.kmea.packages.PackageProcessor; import com.tavultesoft.kmea.util.FileUtils; +import com.tavultesoft.kmea.util.WebViewUtil; import android.graphics.Bitmap; import android.os.Bundle; @@ -135,16 +136,7 @@ public class WebViewFragment extends Fragment implements BlockingStep { @Override public void onPageFinished(WebView view, String url) { // Inject a meta viewport tag into the head of the file if it doesn't exist - webView.loadUrl( - "javascript:(function() {" + - "if(document.head && !document.querySelectorAll('meta[name=viewport]').length) {"+ - "let meta=document.createElement('meta');"+ - "meta.name='viewport';"+ - "meta.content='width=device-width, initial-scale=1';"+ - "document.head.appendChild(meta);"+ - "}"+ - "})()" - ); + WebViewUtil.injectViewport(view); } }); diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java index 5ed8bae829..5fe1652d61 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java @@ -30,6 +30,7 @@ import com.tavultesoft.kmea.packages.PackageProcessor; import com.tavultesoft.kmea.util.FileProviderUtils; import com.tavultesoft.kmea.util.FileUtils; import com.tavultesoft.kmea.util.HelpFile; +import com.tavultesoft.kmea.util.WebViewUtil; import java.io.File; @@ -145,16 +146,7 @@ public class KMHelpFileActivity extends BaseActivity { @Override public void onPageFinished(WebView view, String url) { // Inject a meta viewport tag into the head of the file if it doesn't exist - webView.loadUrl( - "javascript:(function() {" + - "if(document.head && !document.querySelectorAll('meta[name=viewport]').length) {"+ - "let meta=document.createElement('meta');"+ - "meta.name='viewport';"+ - "meta.content='width=device-width, initial-scale=1';"+ - "document.head.appendChild(meta);"+ - "}"+ - "})()" - ); + WebViewUtil.injectViewport(view); } }); diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/util/WebViewUtil.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/util/WebViewUtil.java new file mode 100644 index 0000000000..015280379b --- /dev/null +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/util/WebViewUtil.java @@ -0,0 +1,24 @@ +/** + * Copyright (C) 2021 SIL International. All rights reserved. + */ +package com.tavultesoft.kmea.util; + +import android.webkit.WebView; + +public final class WebViewUtil { + // Inject a meta viewport tag into the head of the file if it doesn't exist + public static void injectViewport(WebView w) { + if (w != null) { + w.loadUrl( + "javascript:(function() {" + + "if(document.head && !document.querySelectorAll('meta[name=viewport]').length) {"+ + "let meta=document.createElement('meta');"+ + "meta.name='viewport';"+ + "meta.content='width=device-width, initial-scale=1';"+ + "document.head.appendChild(meta);"+ + "}"+ + "})()" + ); + } + } +} From eb713ec2741a3e673fc78f740eca9162b8dde680 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 1 Mar 2021 21:46:51 +1100 Subject: [PATCH 057/125] feat(developer): isRTL support for lexical model editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #2556. This adds support for `isRTL`, `insertAfterWord`, and `quotesForKeepSuggestions` punctuation options for lexical models, both in the parsing and in the user interface. A number of options are presented to the user for the quotes and word breaking characters. The parser continues to be a regex-based typescriptish parser. Expect it to be gross. It's too big a job to do properly right now, so it is somewhat limited and will bail if it finds structures it can't understand. I would hope to use something a bit smarter in the future to interface with the editor. Probably when we are actually fully web-based and can depend on a Typescript compiler directly. Please don't get mad at me! 😟😟😟 --- .../Keyman.Developer.UI.UfrmModelEditor.dfm | 110 ++++++++- .../Keyman.Developer.UI.UfrmModelEditor.pas | 185 ++++++++++++++- ...an.Developer.System.LexicalModelParser.pas | 223 +++++++++++++++++- 3 files changed, 500 insertions(+), 18 deletions(-) diff --git a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.dfm b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.dfm index 82d20a01c9..01c331127e 100644 --- a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.dfm +++ b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.dfm @@ -5,6 +5,8 @@ inherited frmModelEditor: TfrmModelEditor ClientHeight = 708 ClientWidth = 712 Font.Name = 'Tahoma' + Position = poDesigned + ExplicitTop = -18 ExplicitWidth = 712 ExplicitHeight = 708 PixelsPerInch = 96 @@ -14,7 +16,7 @@ inherited frmModelEditor: TfrmModelEditor Top = 0 Width = 712 Height = 708 - ActivePage = pageCompile + ActivePage = pageDetails Align = alClient Font.Charset = ANSI_CHARSET Font.Color = clWindowText @@ -48,7 +50,7 @@ inherited frmModelEditor: TfrmModelEditor object panWordlists: TPanel AlignWithMargins = True Left = 4 - Top = 181 + Top = 261 Width = 611 Height = 232 Margins.Left = 4 @@ -58,6 +60,7 @@ inherited frmModelEditor: TfrmModelEditor Align = alTop BevelOuter = bvNone TabOrder = 1 + ExplicitTop = 181 DesignSize = ( 611 232) @@ -131,7 +134,7 @@ inherited frmModelEditor: TfrmModelEditor Left = 4 Top = 6 Width = 611 - Height = 167 + Height = 247 Margins.Left = 4 Margins.Top = 6 Margins.Right = 4 @@ -143,13 +146,14 @@ inherited frmModelEditor: TfrmModelEditor TabOrder = 0 DesignSize = ( 611 - 167) + 247) object lblFormat: TLabel Left = 12 Top = 30 Width = 36 Height = 13 Caption = '&Format' + FocusControl = cbFormat end object lblBasicInformation: TLabel AlignWithMargins = True @@ -174,7 +178,7 @@ inherited frmModelEditor: TfrmModelEditor end object lblComments: TLabel Left = 12 - Top = 83 + Top = 163 Width = 50 Height = 13 Caption = 'Comme&nts' @@ -185,8 +189,40 @@ inherited frmModelEditor: TfrmModelEditor Font.Style = [] ParentFont = False end + object lblInsertAfterWord: TLabel + Left = 12 + Top = 83 + Width = 86 + Height = 13 + Caption = '&Insert after word' + FocusControl = cbInsertAfterWord + end + object lblQuotationMarks: TLabel + Left = 12 + Top = 110 + Width = 86 + Height = 13 + Caption = '&Quotation marks' + FocusControl = cbOpenQuote + end + object lblOpenQuote: TLabel + Left = 106 + Top = 110 + Width = 27 + Height = 13 + Caption = 'open' + FocusControl = cbOpenQuote + end + object lblCloseQuote: TLabel + Left = 220 + Top = 110 + Width = 26 + Height = 13 + Caption = 'close' + FocusControl = cbOpenQuote + end object cbFormat: TComboBox - Left = 98 + Left = 106 Top = 26 Width = 145 Height = 21 @@ -198,7 +234,7 @@ inherited frmModelEditor: TfrmModelEditor 'Custom (custom-1.0)') end object cbWordBreaker: TComboBox - Left = 98 + Left = 106 Top = 53 Width = 145 Height = 21 @@ -211,15 +247,67 @@ inherited frmModelEditor: TfrmModelEditor 'custom') end object memoComments: TMemo - Left = 98 - Top = 80 - Width = 506 + Left = 106 + Top = 160 + Width = 498 Height = 87 Anchors = [akLeft, akTop, akRight] BevelOuter = bvNone - TabOrder = 2 + TabOrder = 6 OnChange = memoCommentsChange end + object cbInsertAfterWord: TComboBox + Left = 106 + Top = 80 + Width = 145 + Height = 21 + TabOrder = 2 + OnChange = cbInsertAfterWordClick + OnClick = cbInsertAfterWordClick + OnKeyUp = cbInsertAfterWordKeyUp + Items.Strings = ( + '(space U+0020)' + '(no word break)' + '(Tibetan tsheg U+0F0B)' + '(Ethiopian wordspace U+1361)') + end + object cbOpenQuote: TComboBox + Left = 139 + Top = 107 + Width = 63 + Height = 21 + TabOrder = 3 + OnChange = cbOpenQuoteClick + OnClick = cbOpenQuoteClick + OnKeyUp = cbOpenQuoteKeyUp + Items.Strings = ( + 'default' + 'ascii' + 'custom') + end + object chkIsRTL: TCheckBox + Left = 106 + Top = 134 + Width = 111 + Height = 17 + Caption = 'Right-to-left script' + TabOrder = 5 + OnClick = chkIsRTLClick + end + object cbCloseQuote: TComboBox + Left = 252 + Top = 107 + Width = 63 + Height = 21 + TabOrder = 4 + OnChange = cbCloseQuoteClick + OnClick = cbCloseQuoteClick + OnKeyUp = cbCloseQuoteKeyUp + Items.Strings = ( + 'default' + 'ascii' + 'custom') + end end end end diff --git a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas index 839abe1343..3bccb71ffd 100644 --- a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas +++ b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas @@ -1,4 +1,4 @@ -unit Keyman.Developer.UI.UfrmModelEditor; +unit Keyman.Developer.UI.UfrmModelEditor; interface @@ -74,6 +74,14 @@ type Label5: TLabel; editOutPath: TEdit; imgQRCode: TImage; + lblInsertAfterWord: TLabel; + cbInsertAfterWord: TComboBox; + lblQuotationMarks: TLabel; + cbOpenQuote: TComboBox; + chkIsRTL: TCheckBox; + cbCloseQuote: TComboBox; + lblOpenQuote: TLabel; + lblCloseQuote: TLabel; procedure FormDestroy(Sender: TObject); procedure cmdAddWordlistClick(Sender: TObject); procedure cmdRemoveWordlistClick(Sender: TObject); @@ -89,6 +97,16 @@ type procedure cmdBrowseTestKeyboardClick(Sender: TObject); procedure editTestKeyboardChange(Sender: TObject); procedure lbDebugHostsClick(Sender: TObject); + procedure cbInsertAfterWordClick(Sender: TObject); + procedure cbInsertAfterWordKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure cbOpenQuoteClick(Sender: TObject); + procedure cbOpenQuoteKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure cbCloseQuoteClick(Sender: TObject); + procedure cbCloseQuoteKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure chkIsRTLClick(Sender: TObject); private type TWordlist = class @@ -161,19 +179,107 @@ begin Result := Ord(format) - 1; // unknown = -1 end; +function FormatFromIndex(index: Integer): TLexicalModelFormat; +begin + Result := TLexicalModelFormat(index+1); // unknown = -1 +end; + function WordBreakerToIndex(wordBreaker: TLexicalModelWordBreaker): Integer; begin Result := Ord(wordBreaker) - 1; // unknown = -1 end; -function FormatFromIndex(format: Integer): TLexicalModelFormat; +function WordBreakerFromIndex(index: Integer): TLexicalModelWordBreaker; begin - Result := TLexicalModelFormat(format+1); // unknown = -1 + Result := TLexicalModelWordBreaker(index+1); // unknown = -1 end; -function WordBreakerFromIndex(wordBreaker: Integer): TLexicalModelWordBreaker; +type + TComboStringOption = record + value, name: string; + class procedure FillCombo(const opts: array of TComboStringOption; + combo: TComboBox); static; + class function GetValue(const opts: array of TComboStringOption; + combo: TComboBox): string; static; + class procedure SetValue(const opts: array of TComboStringOption; + const text: string; combo: TComboBox); static; + end; + +const + CInsertAfterWordOptions: array[0..3] of TComboStringOption = ( + (value: ' '; name: '(space U+0020)'), + (value: ''; name: '(no word break)'), + (value: Char($0F0B); name: '(Tibetan tsheg U+0F0B)'), + (value: Char($1361); name: '(Ethiopian wordspace U+1361)') + ); + + COpenQuoteOptions: array[0..11] of TComboStringOption = ( + (value: TLexicalModelParser.CDefaultOpenQuote), + (value: '«'), + (value: '„'), + (value: '»'), + (value: '"'), + (value: ''''), + (value: '‹'), + (value: '‘'), + (value: '‚'), + (value: '›'), + (value: '「'), + (value: '『') + ); + + CCloseQuoteOptions: array[0..10] of TComboStringOption = ( + (value: TLexicalModelParser.CDefaultCloseQuote), + (value: '»'), + (value: '“'), + (value: '«'), + (value: '"'), + (value: ''''), + (value: '›'), + (value: '’'), + (value: '‹'), + (value: '」'), + (value: '』') + ); + +class procedure TComboStringOption.SetValue(const opts: array of TComboStringOption; const text: string; + combo: TComboBox); +var + index: Integer; begin - Result := TLexicalModelWordBreaker(wordBreaker+1); // unknown = -1 + for index := 0 to High(opts) do + if opts[index].value = text then + begin + combo.ItemIndex := index; + Exit; + end; + combo.Text := text; +end; + +class function TComboStringOption.GetValue(const opts: array of TComboStringOption; + combo: TComboBox): string; +begin + if (combo.ItemIndex >= 0) and (combo.ItemIndex <= High(opts)) + then Result := opts[combo.ItemIndex].value + else Result := combo.Text; +end; + +class procedure TComboStringOption.FillCombo(const opts: array of TComboStringOption; combo: TComboBox); +var + opt: TComboStringOption; +begin + combo.Items.BeginUpdate; + try + combo.Items.Clear; + for opt in opts do + begin + if opt.name = '' + then combo.Items.Add(opt.value) + else combo.Items.Add(opt.name); + end; + finally + combo.Items.EndUpdate; + end; end; { TfrmModelEditor } @@ -269,6 +375,16 @@ begin lblWordBreaker.Enabled := e; cbWordBreaker.Enabled := e; lblComments.Enabled := e; + + lblInsertAfterWord.Enabled := e; + cbInsertAfterWord.Enabled := e; + lblQuotationMarks.Enabled := e; + lblOpenQuote.Enabled := e; + cbOpenQuote.Enabled := e; + lblCloseQuote.Enabled := e; + cbCloseQuote.Enabled := e; + chkIsRTL.Enabled := e; + memoComments.Enabled := e; cmdAddWordlist.Enabled := e; gridWordlists.Enabled := e and (parser.Wordlists.Count > 0); @@ -311,6 +427,10 @@ begin frameSource.OnChanged := SourceChanged; frameSource.TextFileFormat := tffUTF8; + TComboStringOption.FillCombo(CInsertAfterWordOptions, cbInsertAfterWord); + TComboStringOption.FillCombo(COpenQuoteOptions, cbOpenQuote); + TComboStringOption.FillCombo(CCloseQuoteOptions, cbCloseQuote); + pages.ActivePage := pageDetails; finally Dec(FSetup); @@ -459,6 +579,11 @@ var begin cbFormat.ItemIndex := FormatToIndex(parser.Format); cbWordBreaker.ItemIndex := WordBreakerToIndex(parser.WordBreaker); + TComboStringOption.SetValue(CInsertAfterWordOptions, parser.InsertAfterWord, cbInsertAfterWord); + TComboStringOption.SetValue(COpenQuoteOptions, parser.OpenQuote, cbOpenQuote); + TComboStringOption.SetValue(CCloseQuoteOptions, parser.CloseQuote, cbCloseQuote); + chkIsRTL.Checked := parser.IsRTL; + memoComments.Text := parser.Comment; if parser.Wordlists.Count = 0 then @@ -570,6 +695,20 @@ begin Result := WordlistFromTab(pages.ActivePage) <> nil; end; +procedure TfrmModelEditor.cbCloseQuoteClick(Sender: TObject); +begin + if FSetup > 0 then + Exit; + parser.CloseQuote := TComboStringOption.GetValue(CCloseQuoteOptions, cbCloseQuote); + Modified := True; +end; + +procedure TfrmModelEditor.cbCloseQuoteKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + cbCloseQuoteClick(Sender); +end; + procedure TfrmModelEditor.cbFormatClick(Sender: TObject); begin if FSetup > 0 then @@ -578,6 +717,34 @@ begin Modified := True; end; +procedure TfrmModelEditor.cbInsertAfterWordClick(Sender: TObject); +begin + if FSetup > 0 then + Exit; + parser.InsertAfterWord := TComboStringOption.GetValue(CInsertAfterWordOptions, cbInsertAfterWord); + Modified := True; +end; + +procedure TfrmModelEditor.cbInsertAfterWordKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + cbInsertAfterWordClick(Sender); +end; + +procedure TfrmModelEditor.cbOpenQuoteClick(Sender: TObject); +begin + if FSetup > 0 then + Exit; + parser.OpenQuote := TComboStringOption.GetValue(COpenQuoteOptions, cbOpenQuote); + Modified := True; +end; + +procedure TfrmModelEditor.cbOpenQuoteKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + cbOpenQuoteClick(cbOpenQuote); +end; + procedure TfrmModelEditor.cbWordBreakerClick(Sender: TObject); begin if FSetup > 0 then @@ -586,6 +753,14 @@ begin Modified := True; end; +procedure TfrmModelEditor.chkIsRTLClick(Sender: TObject); +begin + if FSetup > 0 then + Exit; + parser.IsRTL := chkIsRTL.Checked; + Modified := True; +end; + procedure TfrmModelEditor.cmdAddWordlistClick(Sender: TObject); begin if dlgAddWordlist.Execute then diff --git a/windows/src/developer/TIKE/main/Keyman.Developer.System.LexicalModelParser.pas b/windows/src/developer/TIKE/main/Keyman.Developer.System.LexicalModelParser.pas index c21d4b5e6f..41e5b3b43b 100644 --- a/windows/src/developer/TIKE/main/Keyman.Developer.System.LexicalModelParser.pas +++ b/windows/src/developer/TIKE/main/Keyman.Developer.System.LexicalModelParser.pas @@ -18,6 +18,10 @@ type FWordlists: TStrings; FWordBreaker: TLexicalModelWordBreaker; FIsEditable: Boolean; + FInsertAfterWord: string; + FIsRTL: Boolean; + FOpenQuote: string; + FCloseQuote: string; function GetText: string; procedure PrepareText; procedure SetComment(const Value: string); @@ -30,6 +34,16 @@ type function ReplaceSources(const m: TMatch): string; function ReplaceComment(const m: TMatch): string; procedure WordlistsChange(Sender: TObject); + function ReplaceInsertAfterWord(const punctuation: string): string; + function ReplaceIsRTL(const punctuation: string): string; + function ReplaceQuotesForKeepSuggestion(const punctuation: string): string; + procedure SetCloseQuote(const Value: string); + procedure SetInsertAfterWord(const Value: string); + procedure SetIsRTL(const Value: Boolean); + procedure SetOpenQuote(const Value: string); + + function JSONEncodeString(const s: string): string; + function JSONDecodeString(s: string): string; public constructor Create(Source: string); destructor Destroy; override; @@ -39,11 +53,23 @@ type property Wordlists: TStrings read FWordlists; property Comment: string read FComment write SetComment; property IsEditable: Boolean read FIsEditable; + property IsRTL: Boolean read FIsRTL write SetIsRTL; + property OpenQuote: string read FOpenQuote write SetOpenQuote; + property CloseQuote: string read FCloseQuote write SetCloseQuote; + property InsertAfterWord: string read FInsertAfterWord write SetInsertAfterWord; + public + // These defaults are defined in common/lexical-model-types/index.d.ts + const + CDefaultInsertAfterWord = ' '; + CDefaultIsRTL = False; + CDefaultOpenQuote = Char($201c); + CDefaultCloseQuote = Char($201d); end; implementation uses + System.JSON, System.SysUtils; { TLexicalModelParser } @@ -55,6 +81,31 @@ const SSources = 'sources\s*:\s*\[(.+?)\]'; SSource = '\s*([''"])(.+?)(\1)\s*(,?)'; + SEndOfObject = '\s*};'; + SPunctuationID = 'punctuation'; + SPunctuation = + ',?\s*'+SPunctuationID+'\s*:\s*{\s*(('+ + '(insertAfterWord\s*:\s*(([''"]).*?(\5)))|'+ + '(isRTL\s*:\s*(true|false))|'+ + '(quotesForKeepSuggestion\s*:\s*{\s*'+ + 'open\s*:\s*(([''"]).*?(\11))\s*,\s*'+ + 'close\s*:\s*(([''"]).*?(\14))\s*})'+ + '),?\s*)*}'; + + // These may delete the entry so we need to take the `,` into account + SInsertAfterWordID = 'insertAfterWord'; + SInsertAfterWord = ',?\s*'+SInsertAfterWordID+'\s*:\s*(([''"]).*?(\2))'; + + SIsRTLID = 'isRTL'; + SIsRTL = ',?\s*'+SIsRTLID+'\s*:\s*(true|false)'; + + SQuotesForKeepSuggestionID = 'quotesForKeepSuggestion'; + SQuotesForKeepSuggestion = + ',?\s*'+SQuotesForKeepSuggestionID+'\s*:\s*{\s*'+ + 'open\s*:\s*(([''"]).*?(\2))\s*,\s*'+ + 'close\s*:\s*(([''"]).*?(\5))\s*'+ + '}'; + constructor TLexicalModelParser.Create(Source: string); begin inherited Create; @@ -79,6 +130,37 @@ begin Result := FText.Text; end; +function TLexicalModelParser.JSONDecodeString(s: string): string; +var + v: TJSONValue; +begin + if s[1] = '''' then + begin + // Javascript can have ' but JSON only " + s[1] := '"'; + s[Length(s)] := '"'; + end; + v := TJSONObject.ParseJSONValue(s); + try + if not v.TryGetValue(Result) then + Result := ''; + finally + v.Free; + end; +end; + +function TLexicalModelParser.JSONEncodeString(const s: string): string; +var + j: TJSONString; +begin + j := TJSONString.Create(s); + try + Result := j.ToJSON; + finally + j.Free; + end; +end; + procedure TLexicalModelParser.Modify; begin FModified := True; @@ -98,6 +180,11 @@ begin FWordBreaker := lmwbUnknown; FComment := ''; + FInsertAfterWord := CDefaultInsertAfterWord; + FIsRTL := CDefaultIsRTL; + FOpenQuote := CDefaultOpenQuote; + FCloseQuote := CDefaultCloseQuote; + s := FText.Text; // /* Comment */ @@ -131,7 +218,40 @@ begin else FIsEditable := False; - FIsEditable := FIsEditable and (FFormat <> lmfUnknown) and (FWordBreaker <> lmwbUnknown); + // insertAfterWord: + m := TRegEx.Match(s, SInsertAfterWord, [roMultiLine]); + if m.Success then + FInsertAfterWord := JSONDecodeString(m.Groups[1].Value) + else if TRegEx.Match(s, SInsertAfterWordID, [roMultiLine]).Success then + FIsEditable := False; + + + // isRTL: true + m := TRegEx.Match(s, SIsRTL, [roMultiLine]); + if m.Success then + FIsRTL := m.Groups[1].Value = 'true' + else if TRegEx.Match(s, SIsRTLID, [roMultiLine]).Success then + FIsEditable := False; + + // quotesForKeepSuggestion: We assume that both open and close are defined, + // in that order. + m := TRegEx.Match(s, SQuotesForKeepSuggestion, [roMultiLine]); + if m.Success then + begin + FOpenQuote := JSONDecodeString(m.Groups[1].Value); + FCloseQuote := JSONDecodeString(m.Groups[4].Value); + end + else if TRegEx.Match(s, SQuotesForKeepSuggestionID, [roMultiLine]).Success then + FIsEditable := False; + + if TRegEx.IsMatch(s, SPunctuationID) and not TRegEx.IsMatch(s, SPunctuation) then + FIsEditable := False; + + + FIsEditable := FIsEditable and + TRegEx.IsMatch(s, SEndOfObject) and + (FFormat <> lmfUnknown) and + (FWordBreaker <> lmwbUnknown); end; function TLexicalModelParser.ReplaceComment(const m: TMatch): string; @@ -173,9 +293,43 @@ begin m.Value.Substring(m.Groups[1].Index - m.Index + m.Groups[1].Length); end; +function TLexicalModelParser.ReplaceInsertAfterWord(const punctuation: string): string; +begin + if punctuation = '' + then Result := '' + else Result := punctuation + ','#13#10; + + Result := Result + + ' insertAfterWord: '+JSONEncodeString(FInsertAfterWord); +end; + +function TLexicalModelParser.ReplaceIsRTL(const punctuation: string): string; +begin + if punctuation = '' + then Result := '' + else Result := punctuation + ','#13#10; + + Result := Result + + ' isRTL: true'; +end; + +function TLexicalModelParser.ReplaceQuotesForKeepSuggestion(const punctuation: string): string; +begin + if punctuation = '' + then Result := '' + else Result := punctuation + ','#13#10; + + Result := Result + + ' quotesForKeepSuggestion: {'#13#10+ + ' open: '+JSONEncodeString(FOpenQuote)+','#13#10+ + ' close: '+JSONEncodeString(FCloseQuote)+#13#10+ + ' }'; +end; + procedure TLexicalModelParser.PrepareText; var - s: string; + punctuation, s: string; + m: TMatch; begin if not FModified then Exit; @@ -186,6 +340,31 @@ begin s := TRegEx.Replace(s, SWordBreaker, ReplaceWordBreaker, [roMultiLine]); s := TRegEx.Replace(s, SSources, ReplaceSources, [roMultiLine]); + s := TRegEx.Replace(s, SPunctuation, '', [roMultiLine]); + + punctuation := ''; + + if FInsertAfterWord <> CDefaultInsertAfterWord then + punctuation := ReplaceInsertAfterWord(punctuation); + + if FIsRTL <> CDefaultIsRTL then + punctuation := ReplaceIsRTL(punctuation); + + if (FOpenQuote <> CDefaultOpenQuote) or (FCloseQuote <> CDefaultCloseQuote) then + punctuation := ReplaceQuotesForKeepSuggestion(punctuation); + + if punctuation <> '' then + begin + punctuation := + ','#13#10+ + ' punctuation: {'+#13#10+ + punctuation + #13#10+ + ' }'; + m := TRegEx.Match(s, SEndOfObject, [roMultiLine]); + if m.Success then + s := s.Substring(0, m.Index - 1) + punctuation + s.Substring(m.Index); + end; + // Replace, remove or add comment if TRegEx.IsMatch(s, SComment, [roSingleLine]) then s := TRegEx.Replace(s, SComment, ReplaceComment, [roSingleLine]) @@ -196,6 +375,16 @@ begin FText.Text := s; end; +procedure TLexicalModelParser.SetCloseQuote(const Value: string); +begin + Assert(FIsEditable); + if FCloseQuote <> Value then + begin + FCloseQuote := Value; + Modify; + end; +end; + procedure TLexicalModelParser.SetComment(const Value: string); begin Assert(FIsEditable); @@ -216,6 +405,36 @@ begin end; end; +procedure TLexicalModelParser.SetInsertAfterWord(const Value: string); +begin + Assert(FIsEditable); + if FInsertAfterWord <> Value then + begin + FInsertAfterWord := Value; + Modify; + end; +end; + +procedure TLexicalModelParser.SetIsRTL(const Value: Boolean); +begin + Assert(FIsEditable); + if FIsRTL <> Value then + begin + FIsRTL := Value; + Modify; + end; +end; + +procedure TLexicalModelParser.SetOpenQuote(const Value: string); +begin + Assert(FIsEditable); + if FOpenQuote <> Value then + begin + FOpenQuote := Value; + Modify; + end; +end; + procedure TLexicalModelParser.SetWordBreaker(const Value: TLexicalModelWordBreaker); begin Assert(FIsEditable); From 3a2e4741bb246ab137cd8b45196d3777daac7cec Mon Sep 17 00:00:00 2001 From: Keyman Build Agent Date: Mon, 1 Mar 2021 13:01:24 -0500 Subject: [PATCH 058/125] auto: increment beta version to 14.0.250 --- HISTORY.md | 5 +++++ VERSION.md | 2 +- .../web/input-processor/package-lock.json | 2 +- common/core/web/input-processor/package.json | 16 +++++++-------- .../web/keyboard-processor/package-lock.json | 2 +- .../core/web/keyboard-processor/package.json | 10 +++++----- .../core/web/tools/recorder/package-lock.json | 2 +- common/core/web/tools/recorder/package.json | 10 +++++----- .../tools/sentry-manager/package-lock.json | 2 +- .../web/tools/sentry-manager/package.json | 4 ++-- common/core/web/utils/package-lock.json | 2 +- common/core/web/utils/package.json | 6 +++--- common/models/templates/package-lock.json | 2 +- common/models/templates/package.json | 8 ++++---- common/models/types/package-lock.json | 2 +- common/models/types/package.json | 2 +- common/models/wordbreakers/package-lock.json | 2 +- common/models/wordbreakers/package.json | 4 ++-- common/predictive-text/package-lock.json | 2 +- common/predictive-text/package.json | 14 ++++++------- developer/js/package-lock.json | 2 +- developer/js/package.json | 8 ++++---- lerna.json | 2 +- resources/gosh/package-lock.json | 2 +- resources/gosh/package.json | 2 +- resources/web-environment/package-lock.json | 2 +- resources/web-environment/package.json | 4 ++-- web/package-lock.json | 2 +- web/package.json | 20 +++++++++---------- 29 files changed, 74 insertions(+), 69 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 734e73bedc..c644839750 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,10 @@ # Keyman Version History +## 14.0.249 beta 2021-03-01 + +* fix(web): mnemonic keystrokes w FF keymapping (#4540) +* chore(ios/app): Adjust help titles for installing custom dictionaries (#4550) + ## 14.0.248 beta 2021-02-26 * fix(common/models): predictions after context reset / caret shift (#4411) diff --git a/VERSION.md b/VERSION.md index 41678eb31d..550d113513 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -14.0.249 \ No newline at end of file +14.0.250 \ No newline at end of file diff --git a/common/core/web/input-processor/package-lock.json b/common/core/web/input-processor/package-lock.json index aa9a04616c..630cc63262 100644 --- a/common/core/web/input-processor/package-lock.json +++ b/common/core/web/input-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.249", + "version": "14.0.250", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/input-processor/package.json b/common/core/web/input-processor/package.json index b9e2dadcec..c7d99e3808 100644 --- a/common/core/web/input-processor/package.json +++ b/common/core/web/input-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.249", + "version": "14.0.250", "description": "The core text and prediction processing engine for KeymanWeb", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/lexical-model-compiler": "^14.0.249", - "@keymanapp/resources-gosh": "^14.0.249", + "@keymanapp/lexical-model-compiler": "^14.0.250", + "@keymanapp/resources-gosh": "^14.0.250", "@types/node": "^11.9.4", "chai": "^4.2.0", "mocha": "^5.2.0", @@ -34,11 +34,11 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.249", - "@keymanapp/lexical-model-layer": "^14.0.249", - "@keymanapp/models-types": "^14.0.249", - "@keymanapp/web-environment": "^14.0.249", - "@keymanapp/web-utils": "^14.0.249", + "@keymanapp/keyboard-processor": "^14.0.250", + "@keymanapp/lexical-model-layer": "^14.0.250", + "@keymanapp/models-types": "^14.0.250", + "@keymanapp/web-environment": "^14.0.250", + "@keymanapp/web-utils": "^14.0.250", "eventemitter3": "^4.0.0" } } diff --git a/common/core/web/keyboard-processor/package-lock.json b/common/core/web/keyboard-processor/package-lock.json index b4ea0a54af..bd5f7ed19c 100644 --- a/common/core/web/keyboard-processor/package-lock.json +++ b/common/core/web/keyboard-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.249", + "version": "14.0.250", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/keyboard-processor/package.json b/common/core/web/keyboard-processor/package.json index 8fea9b6627..b346177df8 100644 --- a/common/core/web/keyboard-processor/package.json +++ b/common/core/web/keyboard-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.249", + "version": "14.0.250", "description": "Core module for Keyman keyboard support in KeymanWeb.", "repository": { "type": "git", @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.249", + "@keymanapp/resources-gosh": "^14.0.250", "chai": "^4.2.0", "mocha": "^5.2.0", "mocha-teamcity-reporter": "^3.0.0", @@ -31,9 +31,9 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/models-types": "^14.0.249", - "@keymanapp/web-environment": "^14.0.249", - "@keymanapp/web-utils": "^14.0.249", + "@keymanapp/models-types": "^14.0.250", + "@keymanapp/web-environment": "^14.0.250", + "@keymanapp/web-utils": "^14.0.250", "@types/node": "^11.9.4", "ts-node": "^8.0.2" } diff --git a/common/core/web/tools/recorder/package-lock.json b/common/core/web/tools/recorder/package-lock.json index aab15117e6..0ca8c63984 100644 --- a/common/core/web/tools/recorder/package-lock.json +++ b/common/core/web/tools/recorder/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.249", + "version": "14.0.250", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/recorder/package.json b/common/core/web/tools/recorder/package.json index e6b6b8f89a..10e21cbaff 100644 --- a/common/core/web/tools/recorder/package.json +++ b/common/core/web/tools/recorder/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.249", + "version": "14.0.250", "description": "Core classes used to develop KeymanWeb test cases based on keystrokes", "main": "index.js", "scripts": { @@ -17,10 +17,10 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.249", - "@keymanapp/models-types": "^14.0.249", - "@keymanapp/web-environment": "^14.0.249", - "@keymanapp/web-utils": "^14.0.249" + "@keymanapp/keyboard-processor": "^14.0.250", + "@keymanapp/models-types": "^14.0.250", + "@keymanapp/web-environment": "^14.0.250", + "@keymanapp/web-utils": "^14.0.250" }, "devDependencies": { "typescript": "^3.8.3" diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json index 7ce4d72804..5f0e70cf74 100644 --- a/common/core/web/tools/sentry-manager/package-lock.json +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.249", + "version": "14.0.250", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json index 72c3dbfbf1..1b9a41b670 100644 --- a/common/core/web/tools/sentry-manager/package.json +++ b/common/core/web/tools/sentry-manager/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.249", + "version": "14.0.250", "description": "Common setup for KeymanWeb's Sentry-based error reporting", "main": "dist/index.js", "scripts": { @@ -20,7 +20,7 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/web-environment": "^14.0.249", + "@keymanapp/web-environment": "^14.0.250", "@sentry/browser": "^5.27.4" } } diff --git a/common/core/web/utils/package-lock.json b/common/core/web/utils/package-lock.json index e2a5b209b8..f80e9a05b6 100644 --- a/common/core/web/utils/package-lock.json +++ b/common/core/web/utils/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.249", + "version": "14.0.250", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/utils/package.json b/common/core/web/utils/package.json index 16dcf8139e..c50220ee9d 100644 --- a/common/core/web/utils/package.json +++ b/common/core/web/utils/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.249", + "version": "14.0.250", "description": "Common utility functions used throughout other Keyman packages", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -20,8 +20,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.249", - "@keymanapp/web-environment": "^14.0.249", + "@keymanapp/resources-gosh": "^14.0.250", + "@keymanapp/web-environment": "^14.0.250", "@types/node": "^14.0.5", "typescript": "^3.8.3" } diff --git a/common/models/templates/package-lock.json b/common/models/templates/package-lock.json index dd8ec471d1..d2559aba4f 100644 --- a/common/models/templates/package-lock.json +++ b/common/models/templates/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.249", + "version": "14.0.250", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/templates/package.json b/common/models/templates/package.json index 193a2fb11c..d3dc56a7e3 100644 --- a/common/models/templates/package.json +++ b/common/models/templates/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.249", + "version": "14.0.250", "description": "Backing model implementations (templates) for Keyman's modelling layer", "keywords": [ "lm", @@ -42,8 +42,8 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.249", - "@keymanapp/web-utils": "^14.0.249", + "@keymanapp/models-types": "^14.0.250", + "@keymanapp/web-utils": "^14.0.250", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "@types/node": "^14.0.4", @@ -52,6 +52,6 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-wordbreakers": "^14.0.249" + "@keymanapp/models-wordbreakers": "^14.0.250" } } diff --git a/common/models/types/package-lock.json b/common/models/types/package-lock.json index 5cca472a85..ee9dc67b52 100644 --- a/common/models/types/package-lock.json +++ b/common/models/types/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.249", + "version": "14.0.250", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/types/package.json b/common/models/types/package.json index c73b07c23d..74cb634a3e 100644 --- a/common/models/types/package.json +++ b/common/models/types/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.249", + "version": "14.0.250", "description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.", "types": "./index.d.ts", "scripts": { diff --git a/common/models/wordbreakers/package-lock.json b/common/models/wordbreakers/package-lock.json index ecd66b56e2..e2259a0a1a 100644 --- a/common/models/wordbreakers/package-lock.json +++ b/common/models/wordbreakers/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.249", + "version": "14.0.250", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json index 0db135b9cd..db23221880 100644 --- a/common/models/wordbreakers/package.json +++ b/common/models/wordbreakers/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.249", + "version": "14.0.250", "description": "Word breakers bundled in the modeling layer", "keywords": [ "lexical model", @@ -40,7 +40,7 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.249", + "@keymanapp/models-types": "^14.0.250", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "chai": "^4.2.0", diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index 8040273886..3bfdf5c4e1 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.249", + "version": "14.0.250", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json index 0a0eab38bd..cebf53d85f 100644 --- a/common/predictive-text/package.json +++ b/common/predictive-text/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.249", + "version": "14.0.250", "description": "Keyman/Predictive Text integration layer", "main": "build/index.js", "scripts": { @@ -21,8 +21,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/models-types": "^14.0.249", - "@keymanapp/resources-gosh": "^14.0.249", + "@keymanapp/models-types": "^14.0.250", + "@keymanapp/resources-gosh": "^14.0.250", "@types/node": "^10.17.21", "chai": "^4.2.0", "karma": "^4.2.0", @@ -46,10 +46,10 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-templates": "^14.0.249", - "@keymanapp/models-wordbreakers": "^14.0.249", - "@keymanapp/web-environment": "^14.0.249", - "@keymanapp/web-utils": "^14.0.249", + "@keymanapp/models-templates": "^14.0.250", + "@keymanapp/models-wordbreakers": "^14.0.250", + "@keymanapp/web-environment": "^14.0.250", + "@keymanapp/web-utils": "^14.0.250", "es6-shim": "^0.35.5", "string.prototype.codepointat": "^0.2.1", "string.prototype.startswith": "^0.2.0" diff --git a/developer/js/package-lock.json b/developer/js/package-lock.json index 04f2ff99ca..2742c9e8f7 100644 --- a/developer/js/package-lock.json +++ b/developer/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.249", + "version": "14.0.250", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/developer/js/package.json b/developer/js/package.json index 57b994aa60..17c5bedf5c 100644 --- a/developer/js/package.json +++ b/developer/js/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.249", + "version": "14.0.250", "description": "Keyman Developer lexical model compiler", "keywords": [ "keyboard", @@ -37,14 +37,14 @@ "node": ">=12.0.0" }, "dependencies": { - "@keymanapp/models-types": "^14.0.249", + "@keymanapp/models-types": "^14.0.250", "commander": "^3.0.0", "typescript": "^3.8.3", "xml2js": "^0.4.19" }, "devDependencies": { - "@keymanapp/models-templates": "^14.0.249", - "@keymanapp/models-wordbreakers": "^14.0.249", + "@keymanapp/models-templates": "^14.0.250", + "@keymanapp/models-wordbreakers": "^14.0.250", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", diff --git a/lerna.json b/lerna.json index 1f68e76621..ef2a6a9206 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "lerna", "typescript" ], - "version": "14.0.249" + "version": "14.0.250" } diff --git a/resources/gosh/package-lock.json b/resources/gosh/package-lock.json index fabe202f9f..707ba3ba1c 100644 --- a/resources/gosh/package-lock.json +++ b/resources/gosh/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.249", + "version": "14.0.250", "lockfileVersion": 1 } diff --git a/resources/gosh/package.json b/resources/gosh/package.json index 550caf282c..66a674131e 100644 --- a/resources/gosh/package.json +++ b/resources/gosh/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.249", + "version": "14.0.250", "description": "Good Ol' Shell", "author": "Marc Durdin (https://github.com/mcdurdin)", "contributors": [ diff --git a/resources/web-environment/package-lock.json b/resources/web-environment/package-lock.json index c66366c27a..6b82ea70bf 100644 --- a/resources/web-environment/package-lock.json +++ b/resources/web-environment/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.249", + "version": "14.0.250", "lockfileVersion": 1 } diff --git a/resources/web-environment/package.json b/resources/web-environment/package.json index 9c7deb0dd3..0ce7341ff3 100644 --- a/resources/web-environment/package.json +++ b/resources/web-environment/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.249", + "version": "14.0.250", "description": "A compilation utility for passing select environment variables into our TS builds", "author": "Joshua Horton (https://github.com/jahorton)", "homepage": "https://github.com/keymanapp/keyman", @@ -17,6 +17,6 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.249" + "@keymanapp/resources-gosh": "^14.0.250" } } diff --git a/web/package-lock.json b/web/package-lock.json index 9c5115b4be..1c894ac471 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.249", + "version": "14.0.250", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/web/package.json b/web/package.json index 8d95311ff7..a271937fc4 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.249", + "version": "14.0.250", "description": "Facilitates text input in any language.", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.249", - "@keymanapp/web-sentry-manager": "^14.0.249", + "@keymanapp/resources-gosh": "^14.0.250", + "@keymanapp/web-sentry-manager": "^14.0.250", "@sentry/cli": "^1.52.3", "chai": "^4.2.0", "google-closure-compiler-java": "^20200224.0.0", @@ -51,13 +51,13 @@ "sentry-cli": "sentry-cli" }, "dependencies": { - "@keymanapp/input-processor": "^14.0.249", - "@keymanapp/keyboard-processor": "^14.0.249", - "@keymanapp/lexical-model-layer": "^14.0.249", - "@keymanapp/models-types": "^14.0.249", - "@keymanapp/recorder-core": "^14.0.249", - "@keymanapp/web-environment": "^14.0.249", - "@keymanapp/web-utils": "^14.0.249", + "@keymanapp/input-processor": "^14.0.250", + "@keymanapp/keyboard-processor": "^14.0.250", + "@keymanapp/lexical-model-layer": "^14.0.250", + "@keymanapp/models-types": "^14.0.250", + "@keymanapp/recorder-core": "^14.0.250", + "@keymanapp/web-environment": "^14.0.250", + "@keymanapp/web-utils": "^14.0.250", "@types/node": "^11.9.4", "es6-shim": "^0.35.5", "eventemitter3": "^4.0.0", From 7ea7ba0e11d2fb290e505f4ca66c291e2d755048 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 2 Mar 2021 09:27:18 +1100 Subject: [PATCH 059/125] fix(developer): track modified state in wordlist editor better Fixes #3337. In some situations, the wordlist editor was not tracking modified state 100% correctly, which meant that edits could be lost. This resolves that by simplifying the modification state tracking. --- ...yman.Developer.UI.UframeWordlistEditor.pas | 28 ++++++++++--------- .../Keyman.Developer.UI.UfrmModelEditor.pas | 7 ++--- ...Keyman.Developer.UI.UfrmWordlistEditor.pas | 2 +- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UframeWordlistEditor.pas b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UframeWordlistEditor.pas index c8739c0e59..152a6f993a 100644 --- a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UframeWordlistEditor.pas +++ b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UframeWordlistEditor.pas @@ -46,7 +46,7 @@ type frameSource: TframeTextEditor; FSetup: Integer; FModified: Boolean; - FOnModifiedChanged: TNotifyEvent; + FOnChanged: TNotifyEvent; FFilename: string; procedure UpdateData; function MoveCodeToWordlist: Boolean; @@ -67,7 +67,7 @@ type function SaveToFile(const Filename: string): Boolean; property Filename: string read FFilename; property Modified: Boolean read FModified write SetModified; - property OnModifiedChanged: TNotifyEvent read FOnModifiedChanged write FOnModifiedChanged; + property OnChanged: TNotifyEvent read FOnChanged write FOnChanged; end; implementation @@ -212,10 +212,9 @@ var w: TWordlistWord; begin if FSetup > 0 then Exit; + Inc(FSetup); try - Modified := True; - if ARow = gridWordlist.RowCount - 1 then begin if (Value = '') or (Value = S_AddRowText) then @@ -232,12 +231,16 @@ begin begin w := FWordlist.Word[ARow-1]; case ACol of - 0: w.Word := Value; - 1: w.Frequency := StrToIntDef(Value, 0); - 2: w.Comment := Value; + 0: if w.Word = Value then Exit else w.Word := Value; + 1: if w.Frequency = StrToIntDef(Value, 0) then Exit else w.Frequency := StrToIntDef(Value, 0); + 2: if w.Comment = Value then Exit else w.Comment := Value; end; FWordlist.Word[ARow-1] := w; end; + + Modified := True; + if Assigned(FOnChanged) then + FOnChanged(Self); finally Dec(FSetup); end; @@ -291,18 +294,17 @@ end; procedure TframeWordlistEditor.SetModified(const Value: Boolean); begin - if FModified <> Value then - begin - FModified := Value; - if Assigned(FOnModifiedChanged) then - FOnModifiedChanged(Self); - end; + FModified := Value; end; procedure TframeWordlistEditor.SourceChanged(Sender: TObject); begin if FSetup = 0 then + begin Modified := True; + if Assigned(FOnChanged) then + FOnChanged(Self); + end; end; procedure TframeWordlistEditor.FillCode; diff --git a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas index 3bccb71ffd..3a156b284e 100644 --- a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas +++ b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas @@ -136,7 +136,7 @@ type function CheckModifiedWordlistsForRemoval( newParser: TLexicalModelParser): Boolean; procedure UpdateQRCode; - procedure WordlistModifiedChanged(Sender: TObject); + procedure WordlistChanged(Sender: TObject); { Private declarations } protected function GetHelpTopic: string; override; @@ -646,13 +646,12 @@ begin Result.Tab.PageControl := pages; Result.Frame := TframeWordlistEditor.Create(Self); + Result.Frame.OnChanged := WordlistChanged; Result.Frame.LoadFromFile(ExtractFilePath(Filename) + WordlistFilename); Result.Frame.Align := alClient; Result.Frame.Parent := Result.Tab; Result.Frame.Visible := True; - - Result.Frame.OnModifiedChanged := WordlistModifiedChanged; end; function TfrmModelEditor.WordlistFromTab( @@ -665,7 +664,7 @@ begin Result := nil; end; -procedure TfrmModelEditor.WordlistModifiedChanged(Sender: TObject); +procedure TfrmModelEditor.WordlistChanged(Sender: TObject); begin // We only go from clean to dirty, not vice-versa, on this notification if (Sender as TframeWordlistEditor).Modified then diff --git a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmWordlistEditor.pas b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmWordlistEditor.pas index b1795f6069..0642b635bc 100644 --- a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmWordlistEditor.pas +++ b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmWordlistEditor.pas @@ -53,7 +53,7 @@ begin frame := TframeWordlistEditor.Create(Self); frame.Align := alClient; frame.Parent := Self; - frame.OnModifiedChanged := FrameModified; + frame.OnChanged := FrameModified; end; procedure TfrmWordlistEditor.FormDestroy(Sender: TObject); From 1f87c7608c7c8246c59260326c4e076dd5d82760 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 2 Mar 2021 11:59:12 +1100 Subject: [PATCH 060/125] fix(developer): require language tag when compiling keyboard package Fixes #2998. Three parts to this: * The package compiler will now give a warning if a keyboard in a package does not contain any language metadata. * Removed old canonicalization check during compile which is now handled better in `TPackageInfoRefreshKeyboards`. * Reduced non-canonical BCP 47 tag warnings in `TPackageInfoRefreshKeyboards` to "info" so they do not block the build. Point 3 above is one part of the outcome of our BCP 47 discussion at: https://docs.google.com/document/d/1N5CkTX7tlLlmUB9Mbd-LblY1ch1e642XxlTasC_WE_Q/edit#heading=h.fiy983iliay5 --- .../global/delphi/general/CompilePackage.pas | 33 +++---------------- ...man.System.PackageInfoRefreshKeyboards.pas | 2 +- 2 files changed, 5 insertions(+), 30 deletions(-) diff --git a/windows/src/global/delphi/general/CompilePackage.pas b/windows/src/global/delphi/general/CompilePackage.pas index 84f937b3ea..edf7532c7f 100644 --- a/windows/src/global/delphi/general/CompilePackage.pas +++ b/windows/src/global/delphi/general/CompilePackage.pas @@ -41,7 +41,6 @@ uses System.IniFiles, System.Zip, - BCP47Tag, OnlineConstants, VisualKeyboard, utilfiletypes, @@ -452,41 +451,17 @@ begin end; const - SKKeyboardPackageLanguageNonCanonical = 'The keyboard %0:s has a non-canonical language ID "%1:s" (%2:s), should be "%3:s".'; + SKKeyboardPackageLanguageNonCanonical = 'The keyboard %0:s has a non-canonical language tag "%1:s" (%2:s), should be "%3:s".'; + SKKeyboardShouldHaveAtLeastOneLanguage = 'The keyboard %0:s has no language tags. It should have at least one language tag.'; procedure TCompilePackage.CheckKeyboardLanguages; var k: TPackageKeyboard; - l: TPackageKeyboardLanguage; - NewID: string; - Tag, NewTag: TBCP47Tag; begin for k in pack.Keyboards do begin - for l in k.Languages do - begin - Tag := TBCP47Tag.Create(l.ID); - NewID := TCanonicalLanguageCodeUtils.FindBestTag(l.ID, False, False); - if NewID = '' then - begin - // We don't have enough data to validate this tag - Continue; - end; - - NewTag := TBCP47Tag.Create(NewID); - try - if (Tag.Script = '') and (NewTag.Script <> '') then - begin - // Only give non-canonical warning if the script tag is missing but should - // be present. - NewTag.Region := Tag.Region; // We don't care about region, don't give unhelpful info to developer - WriteMessage(plsWarning, Format(SKKeyboardPackageLanguageNonCanonical, [k.ID, l.ID, l.Name, NewTag.Tag])); - end; - finally - NewTag.Free; - Tag.Free; - end; - end; + if k.Languages.Count = 0 then + WriteMessage(plsWarning, Format(SKKeyboardShouldHaveAtLeastOneLanguage, [k.ID])); end; end; diff --git a/windows/src/global/delphi/packages/Keyman.System.PackageInfoRefreshKeyboards.pas b/windows/src/global/delphi/packages/Keyman.System.PackageInfoRefreshKeyboards.pas index 87b8102ba2..fa1e1bd245 100644 --- a/windows/src/global/delphi/packages/Keyman.System.PackageInfoRefreshKeyboards.pas +++ b/windows/src/global/delphi/packages/Keyman.System.PackageInfoRefreshKeyboards.pas @@ -209,7 +209,7 @@ begin end else if not TCanonicalLanguageCodeUtils.IsCanonical(tag, msg, False, False) then begin - DoError(Format(SWarning_LanguageTagIsNotCanonical, [kbd.ID, lang.ID, msg]), plsWarning); + DoError(Format(SWarning_LanguageTagIsNotCanonical, [kbd.ID, lang.ID, msg]), plsInfo); end; finally Free; From 36f57886510aa2110cefcc38d201b488fb4647dd Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 2 Mar 2021 12:16:09 +1100 Subject: [PATCH 061/125] fix(developer): Avoid blank keys when importing KMX to KVKS Fixes #3011. Keys that have only whitespace will no longer generate separate key data for the on screen keyboard, as they have no visual presentation anyway. --- .../TIKE/dialogs/UfrmVisualKeyboardImportKMX.pas | 11 ++--------- .../TIKE/main/UframeOnScreenKeyboardEditor.pas | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/windows/src/developer/TIKE/dialogs/UfrmVisualKeyboardImportKMX.pas b/windows/src/developer/TIKE/dialogs/UfrmVisualKeyboardImportKMX.pas index 09a63d6eeb..6c3eb9d54c 100644 --- a/windows/src/developer/TIKE/dialogs/UfrmVisualKeyboardImportKMX.pas +++ b/windows/src/developer/TIKE/dialogs/UfrmVisualKeyboardImportKMX.pas @@ -355,19 +355,14 @@ var vk: TVKKey; n: Integer; begin -// lb.items.Add('AddKey: ENTER'); - if nkey >= keys.count then begin -// lb.items.Add('AddKey: EXIT: bug!'); - exit; + Exit; end; -// lb.items.Add(Format('AddKey: nkey: %d data: "%s" vk: %s ', [nkey, data, VKeyNames[TVKKey(keys[nkey]).vkey]])); - if (data <> '') then //and (nkey < keys.Count) then + if Trim(data) <> '' then begin vk := TVKKey(keys[nkey]); -// ShowMessage(Format('key: %d shift: %d text: "%s"', [vk.vkey, vk.shift, data])); n := FVK.Keys.IndexOf(vk.vkey, vk.shift); if n < 0 then k := TVisualKeyboardKey.Create @@ -379,11 +374,9 @@ begin if n < 0 then FVK.Keys.Add(k); end; -// data := ''; Inc(nkey); PostMessage(Handle, WM_User_SendNextKey, 0, 0); // I4156 -// lb.items.Add('AddKey: Exit (nkey now='+IntToStr(nkey)); end; {------------------------------------------------------------------------------- diff --git a/windows/src/developer/TIKE/main/UframeOnScreenKeyboardEditor.pas b/windows/src/developer/TIKE/main/UframeOnScreenKeyboardEditor.pas index 7db2b78df9..b166d85536 100644 --- a/windows/src/developer/TIKE/main/UframeOnScreenKeyboardEditor.pas +++ b/windows/src/developer/TIKE/main/UframeOnScreenKeyboardEditor.pas @@ -674,7 +674,7 @@ procedure TframeOnScreenKeyboardEditor.cmdVKImportKMXClick(Sender: TObject); if ValidExtShiftStates[j] = [essAlt] then Continue; if ValidExtShiftStates[j] = [essShift, essAlt] then Continue; // I1471:9011 - if (kbdOnScreen.Keys[i].KeyCaps[j] <> '') and + if (Trim(kbdOnScreen.Keys[i].KeyCaps[j]) <> '') and (kbdOnScreen.Keys[i].KeyCaps[j][1] >= #32) then begin k := TVisualKeyboardKey.Create; From b10ae8b0d45ef55e49e50a1b0f834ce9efdae22a Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Tue, 2 Mar 2021 08:41:44 +0700 Subject: [PATCH 062/125] fix(common/resources): Fix help.keyman.com path for CI --- resources/build/help-keyman-com.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/resources/build/help-keyman-com.sh b/resources/build/help-keyman-com.sh index f90e0fe5ad..4128bdbf0b 100755 --- a/resources/build/help-keyman-com.sh +++ b/resources/build/help-keyman-com.sh @@ -70,6 +70,15 @@ fi # Uploading Keyman documentation # +## Determine the help.keyman.com product path based on $platform +function help_product_path { + if [ $platform == 'ios' ]; then + echo "products/iphone-and-ipad/$VERSION_RELEASE" + else + echo "products/$platform/$VERSION_RELEASE" + fi +} + ## ## Upload documentation updates to help.keyman.com ## Paths depend on $platform @@ -82,29 +91,26 @@ function upload_keyman_help { case $platform in android) helppath=$KEYMAN_ROOT/android/help - dstpath="$HELP_KEYMAN_COM/products/android/$VERSION_RELEASE" ;; ios) helppath=$KEYMAN_ROOT/ios/help - dstpath="$HELP_KEYMAN_COM/products/iphone-and-ipad/$VERSION_RELEASE" ;; linux) helppath=$KEYMAN_ROOT/linux/help - dstpath="$HELP_KEYMAN_COM/products/linux/$VERSION_RELEASE" ;; mac) helppath=$KEYMAN_ROOT/mac/help - dstpath="$HELP_KEYMAN_COM/products/mac/$VERSION_RELEASE" ;; windows) # Note: `/windows/src/desktop/help/build.sh web` must be run first helppath=$KEYMAN_ROOT/windows/bin/help/md/desktop - dstpath="$HELP_KEYMAN_COM/products/windows/$VERSION_RELEASE" ;; *) display_usage esac + dstpath="$HELP_KEYMAN_COM/$(help_product_path)" + # # Look for help source folder. # @@ -138,7 +144,7 @@ function commit_and_push { fi local branchname="auto/$platform-help-$VERSION_WITH_TAG" - local modifiedfiles="products/$platform/$VERSION_RELEASE" + local modifiedfiles=$(help_product_path) local basebranch="master" From 85b9e0434532887239346b95636bfc8c5e46c9b9 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 2 Mar 2021 13:26:40 +1100 Subject: [PATCH 063/125] chore(developer): address review comment --- .../TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas index 3bccb71ffd..338a8e2bd7 100644 --- a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas +++ b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas @@ -206,9 +206,10 @@ type end; const - CInsertAfterWordOptions: array[0..3] of TComboStringOption = ( + CInsertAfterWordOptions: array[0..4] of TComboStringOption = ( (value: ' '; name: '(space U+0020)'), (value: ''; name: '(no word break)'), + (value: Char($200B); name: '(Zero Width Space U+200B)'), (value: Char($0F0B); name: '(Tibetan tsheg U+0F0B)'), (value: Char($1361); name: '(Ethiopian wordspace U+1361)') ); From 84c0202d85ee84989a8e9d7f7e950611e1d8820b Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 2 Mar 2021 13:27:17 +1100 Subject: [PATCH 064/125] chore(developer): tweak casing --- .../TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas index 338a8e2bd7..a27c4b3498 100644 --- a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas +++ b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas @@ -207,9 +207,9 @@ type const CInsertAfterWordOptions: array[0..4] of TComboStringOption = ( - (value: ' '; name: '(space U+0020)'), - (value: ''; name: '(no word break)'), - (value: Char($200B); name: '(Zero Width Space U+200B)'), + (value: ' '; name: '(Space U+0020)'), + (value: ''; name: '(No word break)'), + (value: Char($200B); name: '(Zero width space U+200B)'), (value: Char($0F0B); name: '(Tibetan tsheg U+0F0B)'), (value: Char($1361); name: '(Ethiopian wordspace U+1361)') ); From 00c9571db9f29d0463d43aff5828ff185be8064a Mon Sep 17 00:00:00 2001 From: jahorton Date: Tue, 2 Mar 2021 09:30:57 +0700 Subject: [PATCH 065/125] fix(ios): workaround pt 1 - package request shunted to Safari --- .../WebBrowserViewController.swift | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/ios/keyman/Keyman/Keyman/KMWebBrowser/WebBrowserViewController.swift b/ios/keyman/Keyman/Keyman/KMWebBrowser/WebBrowserViewController.swift index bc776cda60..f749022164 100644 --- a/ios/keyman/Keyman/Keyman/KMWebBrowser/WebBrowserViewController.swift +++ b/ios/keyman/Keyman/Keyman/KMWebBrowser/WebBrowserViewController.swift @@ -204,6 +204,15 @@ class WebBrowserViewController: UIViewController, UIWebViewDelegate, UIAlertView func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebView.NavigationType) -> Bool { + if request.url?.lastPathComponent.hasSuffix(".kmp") ?? false { + // The user is trying to download a .kmp, but the standard + // UIWebView can't handle it properly. + // + // Instead, we can just forward that URL back into Safari. + UIApplication.shared.openURL(request.url!) + + return false + } updateAddress(request) return true } @@ -222,13 +231,25 @@ class WebBrowserViewController: UIViewController, UIWebViewDelegate, UIAlertView func webView(_ webView: UIWebView, didFailLoadWithError error: Error) { UIApplication.shared.isNetworkActivityIndicatorVisible = false updateButtons() - let alertController = UIAlertController(title: "Cannot Open Page", - message: error.localizedDescription, - preferredStyle: UIAlertController.Style.alert) - alertController.addAction(UIAlertAction(title: "OK", - style: UIAlertAction.Style.default, - handler: nil)) - self.present(alertController, animated: true, completion: nil) + + var signalError: Bool = true + + // An error will likely result if the user attempts to download a KMP, + // despite the fact that we tell it not to attempt a load. + let nsError = error as NSError + if let url = nsError.userInfo["NSErrorFailingURLKey"] as? NSURL { + signalError = !(url.path?.hasSuffix(".kmp") ?? false) + } + + if signalError { + let alertController = UIAlertController(title: "Cannot Open Page", + message: error.localizedDescription, + preferredStyle: UIAlertController.Style.alert) + alertController.addAction(UIAlertAction(title: "OK", + style: UIAlertAction.Style.default, + handler: nil)) + self.present(alertController, animated: true, completion: nil) + } } private func updateButtons() { From 55e39b8413cb74a417e20fc6f10c535f55b3d1d6 Mon Sep 17 00:00:00 2001 From: jahorton Date: Tue, 2 Mar 2021 14:33:38 +0700 Subject: [PATCH 066/125] fix(ios): app browser kmp downloads --- .../KeymanEngine/Classes/HTTPDownloader.swift | 3 +- .../ResourceDownloadManager.swift | 62 +++++++++++++++++++ ios/keyman/Keyman/Keyman/AppDelegate.swift | 2 +- .../WebBrowserViewController.swift | 27 +++++++- 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/HTTPDownloader.swift b/ios/engine/KMEI/KeymanEngine/Classes/HTTPDownloader.swift index 7a2f0053c0..df2cc7fd02 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/HTTPDownloader.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/HTTPDownloader.swift @@ -19,7 +19,8 @@ protocol HTTPDownloadDelegate: class { class HTTPDownloader: NSObject { var queue: [HTTPDownloadRequest] = [] // TODO: Make unowned - weak var handler: HTTPDownloadDelegate? + /*weak*/ var handler: HTTPDownloadDelegate? // 'weak' interferes with installs + // from the app's browser. var currentRequest: HTTPDownloadRequest? var downloadSession: URLSession! public var userInfo: [String: Any] = [:] diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadManager.swift index f7a43a2dd8..553c69d84e 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadManager.swift @@ -682,4 +682,66 @@ public class ResourceDownloadManager { value: PackageDownloadFailedNotification(packageKey: packageKey, error: error)) } } + + /** + * Designed for downloading KMP files when no metadata is available in advance. + */ + public func downloadRawKMP(from url: URL, handler: @escaping (URL?, Error?) -> Void) { + + // First, we need something to handle the download. + class NoMetadataDelegate: HTTPDownloadDelegate { + private let closure: (URL?, Error?) -> Void + + init(withHandler handler: @escaping (URL?, Error?) -> Void) { + self.closure = handler; + } + + func downloadRequestStarted(_ request: HTTPDownloadRequest) { + // Not relevant + } + + func downloadRequestFinished(_ request: HTTPDownloadRequest) { + if request.responseStatusCode != 200 { + // Possible request error (400 Bad Request, 404 Not Found, etc.) + let error = DownloadError.failed(.responseCode(request.responseStatusCode ?? 400, + request.responseStatusMessage ?? "", + request.url)) + + // Now that we've synthesized an appropriate error instance, use the same handler + // as for HTTPDownloader's 'failed' condition. + downloadRequestFailed(request, with: error) + } else { + self.closure(URL(fileURLWithPath: request.destinationFile!), nil) + } + } + + func downloadRequestFailed(_ request: HTTPDownloadRequest, with error: Error?) { + self.closure(nil, error) + } + + func downloadQueueFinished(_ queue: HTTPDownloader) { + // Not relevant + } + + func downloadQueueCancelled(_ queue: HTTPDownloader) { + // Not relevant, but to be safe... + self.closure(nil, nil) + } + } + + let delegate = NoMetadataDelegate(withHandler: handler) + let downloader = HTTPDownloader(delegate, session: self.session) + let request = HTTPDownloadRequest(url: url, downloadType: .downloadFile) + + // Since we don't know the package key in advance, we'll download it to + // the app's cache directory, then figure everything out once we open it. + let cachesDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] + let tempFilename = url.lastPathComponent + let cachedDest = cachesDir.appendingPathComponent(tempFilename) + + request.destinationFile = cachedDest.path + + downloader.addRequest(request) + downloader.run() + } } diff --git a/ios/keyman/Keyman/Keyman/AppDelegate.swift b/ios/keyman/Keyman/Keyman/AppDelegate.swift index bb09f19918..5d294a51db 100644 --- a/ios/keyman/Keyman/Keyman/AppDelegate.swift +++ b/ios/keyman/Keyman/Keyman/AppDelegate.swift @@ -36,7 +36,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { vc.dismiss(animated: true, completion: nil) if let package = rfm.prepareKMPInstall(from: destinationUrl, alertHost: vc) { - // We choose to prompt the user for comfirmation, rather + // We choose to prompt the user for confirmation, rather // than automatically installing the package. // // Since we're operating at the root, we want to present in a separate UINavigationController. diff --git a/ios/keyman/Keyman/Keyman/KMWebBrowser/WebBrowserViewController.swift b/ios/keyman/Keyman/Keyman/KMWebBrowser/WebBrowserViewController.swift index f749022164..8e6e7f9b53 100644 --- a/ios/keyman/Keyman/Keyman/KMWebBrowser/WebBrowserViewController.swift +++ b/ios/keyman/Keyman/Keyman/KMWebBrowser/WebBrowserViewController.swift @@ -205,11 +205,32 @@ class WebBrowserViewController: UIViewController, UIWebViewDelegate, UIAlertView shouldStartLoadWith request: URLRequest, navigationType: UIWebView.NavigationType) -> Bool { if request.url?.lastPathComponent.hasSuffix(".kmp") ?? false { + // Can't have the browser auto-download with no way to select a different page. + // Can't just ignore it, either, as the .kmp may result from a redirect from + // the previous URL. (Like if using the keyman.com keyboard search!) + let userData = UserDefaults.standard + userData.set(nil as String?, forKey: webBrowserLastURLKey) + userData.synchronize() + // The user is trying to download a .kmp, but the standard // UIWebView can't handle it properly. - // - // Instead, we can just forward that URL back into Safari. - UIApplication.shared.openURL(request.url!) + + ResourceDownloadManager.shared.downloadRawKMP(from: request.url!) { file, error in + // do something! + if let error = error { + let alert = ResourceFileManager.shared.buildSimpleAlert(title: "Error", message: error.localizedDescription) + self.present(alert, animated: true, completion: nil) + return + } + + // Re-use the standard 'open random file' code as when launching the + // app from a file. This will also auto-dismiss the browser, returning + // to the app's main screen. + if let file = file { + let appDelegate = UIApplication.shared.delegate as! AppDelegate + _ = appDelegate.application(UIApplication.shared, open: file) + } + } return false } From 9847e553a5571090d07a426bddbe77b6accc7d88 Mon Sep 17 00:00:00 2001 From: jahorton Date: Tue, 2 Mar 2021 15:18:52 +0700 Subject: [PATCH 067/125] fix(ios): fixes kbd display over welcome.htm --- ios/keyman/Keyman/Keyman/AppDelegate.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ios/keyman/Keyman/Keyman/AppDelegate.swift b/ios/keyman/Keyman/Keyman/AppDelegate.swift index 5d294a51db..90d40bac2f 100644 --- a/ios/keyman/Keyman/Keyman/AppDelegate.swift +++ b/ios/keyman/Keyman/Keyman/AppDelegate.swift @@ -36,6 +36,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { vc.dismiss(animated: true, completion: nil) if let package = rfm.prepareKMPInstall(from: destinationUrl, alertHost: vc) { + // First, explicitly hide the keyboard. Otherwise, the app may try to + // redisplay it before package installation is fully complete. + Manager.shared.hideKeyboard() + // We choose to prompt the user for confirmation, rather // than automatically installing the package. // From e18e3b5d007c60467fa7e50d33156269675fb546 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Tue, 2 Mar 2021 13:43:25 +0700 Subject: [PATCH 068/125] change(android/engine): Refactor webview cleanup --- .../kmapro/KMPBrowserActivity.java | 13 ++------ android/KMEA/app/src/main/AndroidManifest.xml | 2 ++ .../tavultesoft/kmea/KMHelpFileActivity.java | 30 +++++++++---------- .../tavultesoft/kmea/util/WebViewUtil.java | 23 ++++++++++++-- 4 files changed, 39 insertions(+), 29 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java index 406b9eaecc..2a2d47a440 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2020 SIL International. All rights reserved. + * Copyright (C) 2020-2021 SIL International. All rights reserved. */ package com.tavultesoft.kmapro; @@ -23,6 +23,7 @@ import androidx.appcompat.app.AppCompatActivity; import com.tavultesoft.kmea.BaseActivity; import com.tavultesoft.kmea.KMManager; import com.tavultesoft.kmea.util.KMPLink; +import com.tavultesoft.kmea.util.WebViewUtil; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -163,15 +164,7 @@ public class KMPBrowserActivity extends BaseActivity { @Override protected void onDestroy() { super.onDestroy(); - // Blank the webView and destroy completely - if (webView != null) { - webView.loadUrl("about:blank"); - ((ViewGroup) webView.getParent()).removeView(webView); - webView.removeAllViews(); - webView.clearCache(true); - webView.destroy(); - webView = null; - } + WebViewUtil.cleanup(webView); } @Override diff --git a/android/KMEA/app/src/main/AndroidManifest.xml b/android/KMEA/app/src/main/AndroidManifest.xml index 1c8428f73a..439d5a4d54 100644 --- a/android/KMEA/app/src/main/AndroidManifest.xml +++ b/android/KMEA/app/src/main/AndroidManifest.xml @@ -13,6 +13,7 @@ @@ -47,6 +48,7 @@ diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java index 5fe1652d61..5e980bc56d 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java @@ -11,6 +11,7 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; @@ -79,7 +80,7 @@ public class KMHelpFileActivity extends BaseActivity { finishButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - finish(); + finishAfterTransition(); overridePendingTransition(0, android.R.anim.fade_out); } }); @@ -169,24 +170,21 @@ public class KMHelpFileActivity extends BaseActivity { @Override protected void onDestroy() { super.onDestroy(); - // Blank the webView and destroy completely - if (webView != null) { - webView.loadUrl("about:blank"); - ((ViewGroup) webView.getParent()).removeView(webView); - webView.removeAllViews(); - webView.clearCache(true); - webView.destroy(); - webView = null; - } } @Override - public void onBackPressed() { - if (webView != null && webView.canGoBack()) { - webView.goBack(); - } else { - super.onBackPressed(); - finish(); + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + switch (keyCode) { + case KeyEvent.KEYCODE_BACK: + // Dismiss the help file + super.onBackPressed(); + finishAndRemoveTask(); + break; + } } + + return true; } + } diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/util/WebViewUtil.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/util/WebViewUtil.java index 015280379b..43a67083c8 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/util/WebViewUtil.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/util/WebViewUtil.java @@ -3,13 +3,14 @@ */ package com.tavultesoft.kmea.util; +import android.view.ViewGroup; import android.webkit.WebView; public final class WebViewUtil { // Inject a meta viewport tag into the head of the file if it doesn't exist - public static void injectViewport(WebView w) { - if (w != null) { - w.loadUrl( + public static void injectViewport(WebView webView) { + if (webView != null) { + webView.loadUrl( "javascript:(function() {" + "if(document.head && !document.querySelectorAll('meta[name=viewport]').length) {"+ "let meta=document.createElement('meta');"+ @@ -21,4 +22,20 @@ public final class WebViewUtil { ); } } + + // Blank the webView and destroy completely + // Reference: https://stackoverflow.com/questions/17418503/destroy-webview-in-android/17458577#17458577 + public static void cleanup(WebView webView) { + if (webView != null) { + webView.loadUrl("about:blank"); + ViewGroup viewGroup = (ViewGroup) webView.getParent(); + if (viewGroup != null) { + viewGroup.removeView(webView); + } + webView.removeAllViews(); + webView.destroy(); + webView = null; + } + + } } From 64f9f9297a1fe7cf0f20e163975779bf82ce588d Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Mon, 1 Mar 2021 18:46:19 +0100 Subject: [PATCH 069/125] chore(linux): Improve readme Also ignore some more generated files. --- common/core/desktop/.gitignore | 1 + common/core/desktop/doc/BUILDING.md | 38 ++++++++++++++++++++--------- linux/.gitignore | 9 ++++++- linux/ibus-keyman/.gitignore | 4 +++ linux/ibus-keyman/README | 4 ++- linux/ibus-keyman/README.md | 22 +++++++++++++++++ 6 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 linux/ibus-keyman/.gitignore create mode 100644 linux/ibus-keyman/README.md diff --git a/common/core/desktop/.gitignore b/common/core/desktop/.gitignore index a1d99c33ae..cd78b2f40f 100644 --- a/common/core/desktop/.gitignore +++ b/common/core/desktop/.gitignore @@ -1,2 +1,3 @@ .vs/ build*/ +configure/ diff --git a/common/core/desktop/doc/BUILDING.md b/common/core/desktop/doc/BUILDING.md index 066d3adb63..fa9e65340e 100644 --- a/common/core/desktop/doc/BUILDING.md +++ b/common/core/desktop/doc/BUILDING.md @@ -1,6 +1,7 @@ # How to build the keyboard processor ## Prerequisites + - Python 3 - Meson build system. - C++14 or later compiler (VC++ 2017 or later for Windows). @@ -8,46 +9,55 @@ - kmcomp (for tests) -- must be added to path ## Installing Python3 + ### Linux + You will be able to install a python3 package in any reputable recent version of linux using its package manager if it's not already installed. ### Mac OS X + You can get the official installer from the official Python site: -[https://www.python.org/downloads/mac-osx/]() + ### Windows -You can get the official installer from the official Python site: -[https://www.python.org/downloads/windows/]() +You can get the official installer from the official Python site: + ## Installing Meson + Ensure you have Python3 correctly installed and can run the command `pip3`. -``` + +```bash python3 -m pip install meson ``` ## Building ### Building on Windows + For Windows, use `build.bat` -- this handles environment and x86/x64 cross-compiles with Visual Studio 2017+. You may need to set `SDKVER` environement variable to the current Windows SDK version, if it cannot be automatically detected, e.g.: -``` +```DOS set SDKVER=10.0.19041.0 ``` To build: -``` + +```DOS build.bat all ``` ### Building on Linux, macOS + For all other platforms, in your source directory do the following: -``` + +```bash cd desktop meson build --werror cd build @@ -55,24 +65,29 @@ ninja meson test ``` +For a debug build, pass `--buildtype debug` to meson. + ## Note on kmcomp -kmcomp is the command-line compiler from Keyman Developer, available from https://keyman.com/ or -in this repo in /windows/src/developer/kmcomp. The compiler is currently available as a Windows + +kmcomp is the command-line compiler from Keyman Developer, available from or +in this repo in `/windows/src/developer/kmcomp`. The compiler is currently available as a Windows PE executable only, but it does run under WINE. ## Additional configuration notes ### Windows + The search path can be edited through System settings / Advanced system settings / Environment Variables / User environment variables. -If you have Keyman Developer installed, add %KeymanDeveloperPath% to your path. Otherwise, add +If you have Keyman Developer installed, add `%KeymanDeveloperPath%` to your path. Otherwise, add the path where you extracted the kmcomp archive. ### Linux + You need a wrapper `kmcomp` shell script: -``` +```bash #!/bin/bash wine `dirname "$0"`/kmcomp.exe "$@" ``` @@ -81,4 +96,5 @@ Place this in the same folder as you extracted kmcomp.exe, and `chmod +x kmcomp` to the path (e.g. `export PATH=/path/to/kmcomp:$PATH`, which you can add to `.bashrc`) ### macOS + TODO diff --git a/linux/.gitignore b/linux/.gitignore index 0724a21185..f559eeae9d 100644 --- a/linux/.gitignore +++ b/linux/.gitignore @@ -1,28 +1,35 @@ # linux +.deps/ autom4te.cache +*.o *.orig Makefile.in configure aclocal.m4 config.guess config.sub +config.h config.h.in -config.h.in~ +config.log config.rpath +config.status compile depcomp install-sh +libtool ltmain.sh missing test-driver ylwrap INSTALL +Makefile ABOUT-NLS ltoptions.m4 ltsugar.m4 ltversion.m4 lt~obsolete.m4 libtool.m4 +stamp-h1 __pycache__ *.egg-info build-* diff --git a/linux/ibus-keyman/.gitignore b/linux/ibus-keyman/.gitignore new file mode 100644 index 0000000000..b65a78d449 --- /dev/null +++ b/linux/ibus-keyman/.gitignore @@ -0,0 +1,4 @@ +po/POTFILES +src/keyman.xml +src/keyman.xml.in +src/ibus-engine-keyman \ No newline at end of file diff --git a/linux/ibus-keyman/README b/linux/ibus-keyman/README index 01a706f30d..09f7164272 100644 --- a/linux/ibus-keyman/README +++ b/linux/ibus-keyman/README @@ -1 +1,3 @@ -Keyman engine for IBus. +Keyman engine for IBus + +This readme is required by autoconf. Otherwise see README.md. \ No newline at end of file diff --git a/linux/ibus-keyman/README.md b/linux/ibus-keyman/README.md new file mode 100644 index 0000000000..b2a0cc0b2b --- /dev/null +++ b/linux/ibus-keyman/README.md @@ -0,0 +1,22 @@ +# Keyman engine for IBus + +## Requirements + +You need autoconf, autopoint, gettext, automake and libtool to generate the build system. + +Run `./autogen.sh` to run them. + +## Building + +```bash +./autogen.sh +./configure +make +sudo make install +``` + +For a debug build: + +```bash +./configure CPPFLAGS=-DG_MESSAGES_DEBUG CFLAGS="-g -O0" CXXFLAGS="-g -O0" +``` From 2593c7873f22f7f1024e3fe5457c81cb4e9b522c Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Tue, 2 Mar 2021 16:48:01 +0100 Subject: [PATCH 070/125] docs(common): Add readme for unit tests --- common/core/desktop/tests/unit/kmx/README.md | 62 ++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 common/core/desktop/tests/unit/kmx/README.md diff --git a/common/core/desktop/tests/unit/kmx/README.md b/common/core/desktop/tests/unit/kmx/README.md new file mode 100644 index 0000000000..39de91f403 --- /dev/null +++ b/common/core/desktop/tests/unit/kmx/README.md @@ -0,0 +1,62 @@ +# KMX unit tests + +This directory contains unit tests that test the KMX processing. + +The driver (`kmx.cpp`) loads and executes the tests. Each test consist of a +Keyman keyboard source file (`0*.kmn`) and the compiled KMX keyboard. +The source file contains rules and defines the setup, input and expected results. + +## Defining tests in the `.kmn` file + +The top of the file contains comments that define the setup, input and expected results. +Following the comments are the normal rules of a Keyman keyboard. + +### Setup + +- **description**: this is just for commenting what we test here + +```text +c Description: Tests Caps Lock env set +``` + +- **option**: allows to put the system in a defined state. The example below turns + caps-lock off. + +```text +c option: &capsLock=0 +``` + +- **context**: allows to setup a string which serves as context + +```text +c context: +``` + +### Input + +- **keys**: defines one or more virtual keys that will be processed one after the + other + +```text +c keys: [K_1][K_CAPS][K_2][SHIFT K_3][K_4][K_CAPS][K_5][K_CAPS][K_6] +``` + +### Expected Result + +- **expected**: the resulting string of processing the input keys. In the example + below each key press is expected to output the string `'pass.'`. + +```text +c expected: pass.pass.pass.pass.pass.pass. +``` + +## Running the tests + +All tests can be run at once with `meson test`. + +Alternatively it's possible to run a single test with: + +```bash +cd common/core/desktop +build/tests/unit/kmx/kmx 'tests/unit/kmx/038 - punctkeys.kmn' 'tests/unit/kmx/038 - punctkeys.kmx' +``` From 2421af506e7ac354f4e58b291760fb66d83af652 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Tue, 2 Mar 2021 16:53:55 +0100 Subject: [PATCH 071/125] chore(common): write debug output to console We have a `g_debug_ToConsole` variable. However, so far this was always set to true and output to `syslog`. This change modifies the default to `false`, and in that case logs to `syslog`. If `g_debug_ToConsole` is true we no output to the console. When running the KMX unit tests we set `g_debug_ToConsole` to true. --- common/core/desktop/src/kmx/kmx_debug.cpp | 39 ++++++++++--------- common/core/desktop/src/kmx/kmx_processor.cpp | 5 ++- common/core/desktop/src/kmx/kmx_processor.h | 4 +- common/core/desktop/tests/unit/kmx/kmx.cpp | 1 + 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/common/core/desktop/src/kmx/kmx_debug.cpp b/common/core/desktop/src/kmx/kmx_debug.cpp index 46d75ac35d..9c602e064e 100644 --- a/common/core/desktop/src/kmx/kmx_debug.cpp +++ b/common/core/desktop/src/kmx/kmx_debug.cpp @@ -54,25 +54,25 @@ int km::kbp::kmx::DebugLog_1(const char *file, int line, const char *function, c fmtbuf[255] = 0; va_end(vars); - if(g_debug_KeymanLog) { - if(g_debug_ToConsole) { // I3951 - char windowinfo[1024]; - sprintf(windowinfo, - "%ld" TAB //"TickCount" TAB - "%s:%d" TAB //"SourceFile" TAB - "%s" TAB //"Function" - "%s" NL, //"Message" + if (!g_debug_KeymanLog) + return 0; - GetTickCount(), //"TickCount" TAB - file, line, //"SourceFile" TAB - function, //"Function" TAB - fmtbuf); //"Message" -#ifdef _USE_WINDOWS - std::cout << windowinfo << std::endl; // OutputDebugStringA(windowinfo); -#else - syslog(LOG_DEBUG, "%s", windowinfo); -#endif - } + char windowinfo[1024]; + sprintf(windowinfo, + "%ld" TAB //"TickCount" TAB + "%s:%d" TAB //"SourceFile" TAB + "%s" TAB //"Function" + "%s" NL, //"Message" + + GetTickCount(), //"TickCount" TAB + file, line, //"SourceFile" TAB + function, //"Function" TAB + fmtbuf); //"Message" + + if (g_debug_ToConsole) { // I3951 + std::cout << windowinfo << std::endl; // OutputDebugStringA(windowinfo); + } else { + syslog(LOG_DEBUG, "%s", windowinfo); } return 0; @@ -125,7 +125,8 @@ const char *km::kbp::kmx::Debug_UnicodeString(PKMX_WCHAR s, int x) { bufout[x][0] = 0; for (p = s, q = bufout[x]; *p && (p - s < 128); p++) { - sprintf(q, "U+%4.4X ", *p); q = strchr(q, 0); + sprintf(q, "U+%4.4X ", *p); + q = strchr(q, 0); } //WideCharToMultiByte(CP_ACP, 0, buf, -1, bufout, 128, NULL, NULL); return bufout[x]; diff --git a/common/core/desktop/src/kmx/kmx_processor.cpp b/common/core/desktop/src/kmx/kmx_processor.cpp index 9a5dc643bd..01dfbc6e59 100644 --- a/common/core/desktop/src/kmx/kmx_processor.cpp +++ b/common/core/desktop/src/kmx/kmx_processor.cpp @@ -10,7 +10,8 @@ using namespace kmx; /* Globals */ -KMX_BOOL km::kbp::kmx::g_debug_ToConsole = TRUE, km::kbp::kmx::g_debug_KeymanLog = TRUE; +KMX_BOOL km::kbp::kmx::g_debug_ToConsole = FALSE; +KMX_BOOL km::kbp::kmx::g_debug_KeymanLog = TRUE; KMX_BOOL km::kbp::kmx::g_silent = FALSE; /* @@ -120,7 +121,7 @@ KMX_BOOL KMX_Processor::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke) PKMX_WCHAR p; int sdmfI; - /* + /* If the number of nested groups goes higher than 50, then break out - this is a limitation of stack size. This is basically a catch-all for freaky apps that cause message loopbacks and nasty things like that. Okay, it's really a catch all diff --git a/common/core/desktop/src/kmx/kmx_processor.h b/common/core/desktop/src/kmx/kmx_processor.h index 52ec63b889..7a4ee36e32 100644 --- a/common/core/desktop/src/kmx/kmx_processor.h +++ b/common/core/desktop/src/kmx/kmx_processor.h @@ -128,10 +128,8 @@ const char *Debug_UnicodeString(PKMX_WCHAR s, int x = 0); const char *Debug_UnicodeString(std::u16string s, int x = 0); const char *Debug_ModifierName(KMX_UINT modifiers); - //inline KMX_BOOL ShouldDebug(); - inline KMX_BOOL ShouldDebug() { - return TRUE; // g_debug_KeymanLog; + return g_debug_KeymanLog; } diff --git a/common/core/desktop/tests/unit/kmx/kmx.cpp b/common/core/desktop/tests/unit/kmx/kmx.cpp index 32ca104e8f..bcb047f8d1 100644 --- a/common/core/desktop/tests/unit/kmx/kmx.cpp +++ b/common/core/desktop/tests/unit/kmx/kmx.cpp @@ -510,5 +510,6 @@ int main(int argc, char *argv[]) return 1; } + km::kbp::kmx::g_debug_ToConsole = TRUE; return run_test(argv[1], argv[2]); } From 268a42cba4dc4169b9f46e9a9c9f848288c927b0 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Tue, 2 Mar 2021 17:37:17 +0100 Subject: [PATCH 072/125] chore(common): Fix Windows build --- common/core/desktop/src/kmx/kmx_debug.cpp | 4 ++++ common/core/desktop/src/kmx/kmx_file.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/common/core/desktop/src/kmx/kmx_debug.cpp b/common/core/desktop/src/kmx/kmx_debug.cpp index 9c602e064e..a58fd8e17e 100644 --- a/common/core/desktop/src/kmx/kmx_debug.cpp +++ b/common/core/desktop/src/kmx/kmx_debug.cpp @@ -72,7 +72,11 @@ int km::kbp::kmx::DebugLog_1(const char *file, int line, const char *function, c if (g_debug_ToConsole) { // I3951 std::cout << windowinfo << std::endl; // OutputDebugStringA(windowinfo); } else { +#ifdef _USE_WINDOWS + std::cout << windowinfo << std::endl; // OutputDebugStringA(windowinfo); +#else syslog(LOG_DEBUG, "%s", windowinfo); +#endif } return 0; diff --git a/common/core/desktop/src/kmx/kmx_file.cpp b/common/core/desktop/src/kmx/kmx_file.cpp index 945186a172..81cd36cf0e 100644 --- a/common/core/desktop/src/kmx/kmx_file.cpp +++ b/common/core/desktop/src/kmx/kmx_file.cpp @@ -82,7 +82,7 @@ KMX_BOOL KMX_Processor::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKe LPKEYBOARD kbp; PKMX_BYTE filebase; - DebugLog("Loading file %ws", fileName); + DebugLog("Loading file '%s'", fileName); if(!fileName || !lpKeyboard) { DebugLog("Bad Filename"); From 2f5c001c617109eaf1705ecac8417140fe3df106 Mon Sep 17 00:00:00 2001 From: Keyman Build Agent Date: Tue, 2 Mar 2021 13:01:09 -0500 Subject: [PATCH 073/125] auto: increment beta version to 14.0.251 --- HISTORY.md | 4 ++++ VERSION.md | 2 +- .../web/input-processor/package-lock.json | 2 +- common/core/web/input-processor/package.json | 16 +++++++-------- .../web/keyboard-processor/package-lock.json | 2 +- .../core/web/keyboard-processor/package.json | 10 +++++----- .../core/web/tools/recorder/package-lock.json | 2 +- common/core/web/tools/recorder/package.json | 10 +++++----- .../tools/sentry-manager/package-lock.json | 2 +- .../web/tools/sentry-manager/package.json | 4 ++-- common/core/web/utils/package-lock.json | 2 +- common/core/web/utils/package.json | 6 +++--- common/models/templates/package-lock.json | 2 +- common/models/templates/package.json | 8 ++++---- common/models/types/package-lock.json | 2 +- common/models/types/package.json | 2 +- common/models/wordbreakers/package-lock.json | 2 +- common/models/wordbreakers/package.json | 4 ++-- common/predictive-text/package-lock.json | 2 +- common/predictive-text/package.json | 14 ++++++------- developer/js/package-lock.json | 2 +- developer/js/package.json | 8 ++++---- lerna.json | 2 +- resources/gosh/package-lock.json | 2 +- resources/gosh/package.json | 2 +- resources/web-environment/package-lock.json | 2 +- resources/web-environment/package.json | 4 ++-- web/package-lock.json | 2 +- web/package.json | 20 +++++++++---------- 29 files changed, 73 insertions(+), 69 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index c644839750..6bb3b2d72a 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,9 @@ # Keyman Version History +## 14.0.250 beta 2021-03-02 + +* fix(common/resources): Fix help.keyman.com path for CI (#4565) + ## 14.0.249 beta 2021-03-01 * fix(web): mnemonic keystrokes w FF keymapping (#4540) diff --git a/VERSION.md b/VERSION.md index 550d113513..169475d9ee 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -14.0.250 \ No newline at end of file +14.0.251 \ No newline at end of file diff --git a/common/core/web/input-processor/package-lock.json b/common/core/web/input-processor/package-lock.json index 630cc63262..606905be55 100644 --- a/common/core/web/input-processor/package-lock.json +++ b/common/core/web/input-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.250", + "version": "14.0.251", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/input-processor/package.json b/common/core/web/input-processor/package.json index c7d99e3808..45b23e073e 100644 --- a/common/core/web/input-processor/package.json +++ b/common/core/web/input-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.250", + "version": "14.0.251", "description": "The core text and prediction processing engine for KeymanWeb", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/lexical-model-compiler": "^14.0.250", - "@keymanapp/resources-gosh": "^14.0.250", + "@keymanapp/lexical-model-compiler": "^14.0.251", + "@keymanapp/resources-gosh": "^14.0.251", "@types/node": "^11.9.4", "chai": "^4.2.0", "mocha": "^5.2.0", @@ -34,11 +34,11 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.250", - "@keymanapp/lexical-model-layer": "^14.0.250", - "@keymanapp/models-types": "^14.0.250", - "@keymanapp/web-environment": "^14.0.250", - "@keymanapp/web-utils": "^14.0.250", + "@keymanapp/keyboard-processor": "^14.0.251", + "@keymanapp/lexical-model-layer": "^14.0.251", + "@keymanapp/models-types": "^14.0.251", + "@keymanapp/web-environment": "^14.0.251", + "@keymanapp/web-utils": "^14.0.251", "eventemitter3": "^4.0.0" } } diff --git a/common/core/web/keyboard-processor/package-lock.json b/common/core/web/keyboard-processor/package-lock.json index bd5f7ed19c..3918ab5b71 100644 --- a/common/core/web/keyboard-processor/package-lock.json +++ b/common/core/web/keyboard-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.250", + "version": "14.0.251", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/keyboard-processor/package.json b/common/core/web/keyboard-processor/package.json index b346177df8..730aff8d8e 100644 --- a/common/core/web/keyboard-processor/package.json +++ b/common/core/web/keyboard-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.250", + "version": "14.0.251", "description": "Core module for Keyman keyboard support in KeymanWeb.", "repository": { "type": "git", @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.250", + "@keymanapp/resources-gosh": "^14.0.251", "chai": "^4.2.0", "mocha": "^5.2.0", "mocha-teamcity-reporter": "^3.0.0", @@ -31,9 +31,9 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/models-types": "^14.0.250", - "@keymanapp/web-environment": "^14.0.250", - "@keymanapp/web-utils": "^14.0.250", + "@keymanapp/models-types": "^14.0.251", + "@keymanapp/web-environment": "^14.0.251", + "@keymanapp/web-utils": "^14.0.251", "@types/node": "^11.9.4", "ts-node": "^8.0.2" } diff --git a/common/core/web/tools/recorder/package-lock.json b/common/core/web/tools/recorder/package-lock.json index 0ca8c63984..1fd6b9f20e 100644 --- a/common/core/web/tools/recorder/package-lock.json +++ b/common/core/web/tools/recorder/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.250", + "version": "14.0.251", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/recorder/package.json b/common/core/web/tools/recorder/package.json index 10e21cbaff..82daae28ec 100644 --- a/common/core/web/tools/recorder/package.json +++ b/common/core/web/tools/recorder/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.250", + "version": "14.0.251", "description": "Core classes used to develop KeymanWeb test cases based on keystrokes", "main": "index.js", "scripts": { @@ -17,10 +17,10 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.250", - "@keymanapp/models-types": "^14.0.250", - "@keymanapp/web-environment": "^14.0.250", - "@keymanapp/web-utils": "^14.0.250" + "@keymanapp/keyboard-processor": "^14.0.251", + "@keymanapp/models-types": "^14.0.251", + "@keymanapp/web-environment": "^14.0.251", + "@keymanapp/web-utils": "^14.0.251" }, "devDependencies": { "typescript": "^3.8.3" diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json index 5f0e70cf74..8ad6572a32 100644 --- a/common/core/web/tools/sentry-manager/package-lock.json +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.250", + "version": "14.0.251", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json index 1b9a41b670..7c013e3671 100644 --- a/common/core/web/tools/sentry-manager/package.json +++ b/common/core/web/tools/sentry-manager/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.250", + "version": "14.0.251", "description": "Common setup for KeymanWeb's Sentry-based error reporting", "main": "dist/index.js", "scripts": { @@ -20,7 +20,7 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/web-environment": "^14.0.250", + "@keymanapp/web-environment": "^14.0.251", "@sentry/browser": "^5.27.4" } } diff --git a/common/core/web/utils/package-lock.json b/common/core/web/utils/package-lock.json index f80e9a05b6..2125b91a9c 100644 --- a/common/core/web/utils/package-lock.json +++ b/common/core/web/utils/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.250", + "version": "14.0.251", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/utils/package.json b/common/core/web/utils/package.json index c50220ee9d..d0ccc0bc10 100644 --- a/common/core/web/utils/package.json +++ b/common/core/web/utils/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.250", + "version": "14.0.251", "description": "Common utility functions used throughout other Keyman packages", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -20,8 +20,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.250", - "@keymanapp/web-environment": "^14.0.250", + "@keymanapp/resources-gosh": "^14.0.251", + "@keymanapp/web-environment": "^14.0.251", "@types/node": "^14.0.5", "typescript": "^3.8.3" } diff --git a/common/models/templates/package-lock.json b/common/models/templates/package-lock.json index d2559aba4f..de988f7768 100644 --- a/common/models/templates/package-lock.json +++ b/common/models/templates/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.250", + "version": "14.0.251", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/templates/package.json b/common/models/templates/package.json index d3dc56a7e3..703e84670f 100644 --- a/common/models/templates/package.json +++ b/common/models/templates/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.250", + "version": "14.0.251", "description": "Backing model implementations (templates) for Keyman's modelling layer", "keywords": [ "lm", @@ -42,8 +42,8 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.250", - "@keymanapp/web-utils": "^14.0.250", + "@keymanapp/models-types": "^14.0.251", + "@keymanapp/web-utils": "^14.0.251", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "@types/node": "^14.0.4", @@ -52,6 +52,6 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-wordbreakers": "^14.0.250" + "@keymanapp/models-wordbreakers": "^14.0.251" } } diff --git a/common/models/types/package-lock.json b/common/models/types/package-lock.json index ee9dc67b52..3adf27f054 100644 --- a/common/models/types/package-lock.json +++ b/common/models/types/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.250", + "version": "14.0.251", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/types/package.json b/common/models/types/package.json index 74cb634a3e..6d18e1cd7c 100644 --- a/common/models/types/package.json +++ b/common/models/types/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.250", + "version": "14.0.251", "description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.", "types": "./index.d.ts", "scripts": { diff --git a/common/models/wordbreakers/package-lock.json b/common/models/wordbreakers/package-lock.json index e2259a0a1a..03f67cbcd9 100644 --- a/common/models/wordbreakers/package-lock.json +++ b/common/models/wordbreakers/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.250", + "version": "14.0.251", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json index db23221880..ec513d1cad 100644 --- a/common/models/wordbreakers/package.json +++ b/common/models/wordbreakers/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.250", + "version": "14.0.251", "description": "Word breakers bundled in the modeling layer", "keywords": [ "lexical model", @@ -40,7 +40,7 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.250", + "@keymanapp/models-types": "^14.0.251", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "chai": "^4.2.0", diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index 3bfdf5c4e1..bdda591363 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.250", + "version": "14.0.251", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json index cebf53d85f..8b1f07dcd0 100644 --- a/common/predictive-text/package.json +++ b/common/predictive-text/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.250", + "version": "14.0.251", "description": "Keyman/Predictive Text integration layer", "main": "build/index.js", "scripts": { @@ -21,8 +21,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/models-types": "^14.0.250", - "@keymanapp/resources-gosh": "^14.0.250", + "@keymanapp/models-types": "^14.0.251", + "@keymanapp/resources-gosh": "^14.0.251", "@types/node": "^10.17.21", "chai": "^4.2.0", "karma": "^4.2.0", @@ -46,10 +46,10 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-templates": "^14.0.250", - "@keymanapp/models-wordbreakers": "^14.0.250", - "@keymanapp/web-environment": "^14.0.250", - "@keymanapp/web-utils": "^14.0.250", + "@keymanapp/models-templates": "^14.0.251", + "@keymanapp/models-wordbreakers": "^14.0.251", + "@keymanapp/web-environment": "^14.0.251", + "@keymanapp/web-utils": "^14.0.251", "es6-shim": "^0.35.5", "string.prototype.codepointat": "^0.2.1", "string.prototype.startswith": "^0.2.0" diff --git a/developer/js/package-lock.json b/developer/js/package-lock.json index 2742c9e8f7..927aefebd5 100644 --- a/developer/js/package-lock.json +++ b/developer/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.250", + "version": "14.0.251", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/developer/js/package.json b/developer/js/package.json index 17c5bedf5c..950be10f75 100644 --- a/developer/js/package.json +++ b/developer/js/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.250", + "version": "14.0.251", "description": "Keyman Developer lexical model compiler", "keywords": [ "keyboard", @@ -37,14 +37,14 @@ "node": ">=12.0.0" }, "dependencies": { - "@keymanapp/models-types": "^14.0.250", + "@keymanapp/models-types": "^14.0.251", "commander": "^3.0.0", "typescript": "^3.8.3", "xml2js": "^0.4.19" }, "devDependencies": { - "@keymanapp/models-templates": "^14.0.250", - "@keymanapp/models-wordbreakers": "^14.0.250", + "@keymanapp/models-templates": "^14.0.251", + "@keymanapp/models-wordbreakers": "^14.0.251", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", diff --git a/lerna.json b/lerna.json index ef2a6a9206..72e696c937 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "lerna", "typescript" ], - "version": "14.0.250" + "version": "14.0.251" } diff --git a/resources/gosh/package-lock.json b/resources/gosh/package-lock.json index 707ba3ba1c..c431bbc703 100644 --- a/resources/gosh/package-lock.json +++ b/resources/gosh/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.250", + "version": "14.0.251", "lockfileVersion": 1 } diff --git a/resources/gosh/package.json b/resources/gosh/package.json index 66a674131e..4335896d44 100644 --- a/resources/gosh/package.json +++ b/resources/gosh/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.250", + "version": "14.0.251", "description": "Good Ol' Shell", "author": "Marc Durdin (https://github.com/mcdurdin)", "contributors": [ diff --git a/resources/web-environment/package-lock.json b/resources/web-environment/package-lock.json index 6b82ea70bf..5f7ed75bdc 100644 --- a/resources/web-environment/package-lock.json +++ b/resources/web-environment/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.250", + "version": "14.0.251", "lockfileVersion": 1 } diff --git a/resources/web-environment/package.json b/resources/web-environment/package.json index 0ce7341ff3..975e8ae3d8 100644 --- a/resources/web-environment/package.json +++ b/resources/web-environment/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.250", + "version": "14.0.251", "description": "A compilation utility for passing select environment variables into our TS builds", "author": "Joshua Horton (https://github.com/jahorton)", "homepage": "https://github.com/keymanapp/keyman", @@ -17,6 +17,6 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.250" + "@keymanapp/resources-gosh": "^14.0.251" } } diff --git a/web/package-lock.json b/web/package-lock.json index 1c894ac471..3e710d06be 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.250", + "version": "14.0.251", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/web/package.json b/web/package.json index a271937fc4..2f761e88e0 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.250", + "version": "14.0.251", "description": "Facilitates text input in any language.", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.250", - "@keymanapp/web-sentry-manager": "^14.0.250", + "@keymanapp/resources-gosh": "^14.0.251", + "@keymanapp/web-sentry-manager": "^14.0.251", "@sentry/cli": "^1.52.3", "chai": "^4.2.0", "google-closure-compiler-java": "^20200224.0.0", @@ -51,13 +51,13 @@ "sentry-cli": "sentry-cli" }, "dependencies": { - "@keymanapp/input-processor": "^14.0.250", - "@keymanapp/keyboard-processor": "^14.0.250", - "@keymanapp/lexical-model-layer": "^14.0.250", - "@keymanapp/models-types": "^14.0.250", - "@keymanapp/recorder-core": "^14.0.250", - "@keymanapp/web-environment": "^14.0.250", - "@keymanapp/web-utils": "^14.0.250", + "@keymanapp/input-processor": "^14.0.251", + "@keymanapp/keyboard-processor": "^14.0.251", + "@keymanapp/lexical-model-layer": "^14.0.251", + "@keymanapp/models-types": "^14.0.251", + "@keymanapp/recorder-core": "^14.0.251", + "@keymanapp/web-environment": "^14.0.251", + "@keymanapp/web-utils": "^14.0.251", "@types/node": "^11.9.4", "es6-shim": "^0.35.5", "eventemitter3": "^4.0.0", From 958fb3ac1fade5540d2dcd22b4affc086feef537 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Tue, 2 Mar 2021 19:16:06 +0100 Subject: [PATCH 074/125] docs(common): More readme improvements --- .../core/desktop/doc/markdown_files/index.md | 23 ++++++++++++++----- .../include/keyman/keyboardprocessor.h.in | 8 +++---- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/common/core/desktop/doc/markdown_files/index.md b/common/core/desktop/doc/markdown_files/index.md index b584842896..98870c356d 100644 --- a/common/core/desktop/doc/markdown_files/index.md +++ b/common/core/desktop/doc/markdown_files/index.md @@ -1,6 +1,7 @@ # Keyman Keyboard Processor API ## Requirements + 1. Cross platform. 2. Cross language. 3. Facilitate stateless operation of the Engine. @@ -9,8 +10,8 @@ 6. Support querying Keyboard attributes. 7. Idempotent - ## Design decisions in support of requirements: + - Use C or C99 types and calling convention for the interface, it has the broadest language FFI support. [1,2] - Have client (platform glue) code load keyboards, manage & pass state. [3,4,7] @@ -18,8 +19,8 @@ engine [5,6] - Provide get/set calls for client accessible keyboard state information [3,4] - ## Glossary + - __Platform layer:__ the code that consumes the Keyman Keyboard Processor API, and provides the operating system-specific handling of keystroke events and integration with @@ -29,22 +30,32 @@ the application that has the focus and receives text events from the Platform layer. - __Context:__ Text preceding the insertion point - __Marker:__ Positional state that can be placed in the Context. -- __Keyboard:__ A set of rules for execution my an Engine +- __Keyboard:__ A set of rules for execution by an Engine - __Option:__ A variable in a dynamic or static key value store. - __Processor:__ The component that implements this API and can parse and execute a particular keyboard. -- __State:__ An object that hold internal state of the Processor for a given +- __State:__ An object that holds internal state of the Processor for a given insertion point - __Action:__ A directive output by the processor detailing how the Platform layer should transform the Client Application's text buffer. There may be several items produced by a single keyboard event. - __Keyboard Event:__ -A virtual key board event and modifier map recevied from the platform to be +A virtual key board event and modifier map recevied from the Platform layer to be processed with the state object for this Client application. - +- __Virtual Key:__ +A code based on the US English layout, with values matching the Windows +virtual key codes. See `keyboardprocessor_vkeys.h` for definitions. +- __Modifier Key:__ +The set of Control, Shift, Alt, Caps Lock keys. On some platforms these may +have other names (e.g. Alt is called Option on macOS); other platform-specific +modifiers such as Windows key are excluded from this set. Some modifiers are +transient, such as Control, and others have long-lasting state, such as +Caps Lock. ## API + ### Namespace + All calls, types and enums are prefixed with the namespace identifier `km_kbp_` diff --git a/common/core/desktop/include/keyman/keyboardprocessor.h.in b/common/core/desktop/include/keyman/keyboardprocessor.h.in index 2f31bb3654..ce86adb5a0 100644 --- a/common/core/desktop/include/keyman/keyboardprocessor.h.in +++ b/common/core/desktop/include/keyman/keyboardprocessor.h.in @@ -41,23 +41,23 @@ The application that has the focus and receives text events from the Platform layer. - __Context:__ Text preceding the insertion point - __Marker:__ Positional state that can be placed in the Context. -- __Keyboard:__ A set of rules for execution my an Engine +- __Keyboard:__ A set of rules for execution by an Engine - __Option:__ A variable in a dynamic or static key value store. - __Processor:__ The component that implements this API and can parse and execute a particular keyboard. -- __State:__ An object that hold internal state of the Processor for a given +- __State:__ An object that holds internal state of the Processor for a given insertion point - __Action:__ A directive output by the processor detailing how the Platform layer should transform the Client Application's text buffer. There may be several items produced by a single keyboard event. - __Keyboard Event:__ -A virtual key event and modifier map received from the platform to be +A virtual key event and modifier map received from the Platform layer to be processed with the state object for this Client application. - __Virtual Key:__ A code based on the US English layout, with values matching the Windows -virtual key codes. See keyboardprocessor_vkeys.h for definitions. +virtual key codes. See `keyboardprocessor_vkeys.h` for definitions. - __Modifier Key:__ The set of Control, Shift, Alt, Caps Lock keys. On some platforms these may have other names (e.g. Alt is called Option on macOS); other platform-specific From 2e61cb8e325c9cc057cc4790dba59ba207bfc25a Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Wed, 3 Mar 2021 10:05:04 +1100 Subject: [PATCH 075/125] fix(developer): improve CEF location search stability Fixes #4278. When Keyman Developer is installed without Keyman for Windows, the program silently crashes, because it is looking for libcef.dll in the Keyman Desktop install folder rather than using its own copy of it. This improves the search for libcef.dll and kmbrowserhost.exe, and includes kmbrowserhost.exe with the Keyman Developer installation. In the future, we could consider moving kmbrowserhost.exe into a common folder (perhaps when we split Keyman for Windows and Keyman Developer projects in 15.0). I threw in a one-liner addressing a deprecated function warning in the Developer build. --- windows/src/developer/TIKE/Tike.dpr | 43 +++++---- windows/src/developer/inst/kmdev.wxs | 4 + .../src/global/delphi/general/KeymanPaths.pas | 90 +++++++++++++++---- ...n.Developer.System.LexicalModelCompile.pas | 2 +- 4 files changed, 101 insertions(+), 38 deletions(-) diff --git a/windows/src/developer/TIKE/Tike.dpr b/windows/src/developer/TIKE/Tike.dpr index 401d6b6493..3977f72f49 100644 --- a/windows/src/developer/TIKE/Tike.dpr +++ b/windows/src/developer/TIKE/Tike.dpr @@ -302,27 +302,32 @@ const begin TKeymanSentryClient.Start(TSentryClientVcl, kscpDeveloper, LOGGER_DEVELOPER_IDE_TIKE); try - CoInitFlags := COINIT_APARTMENTTHREADED; - - FInitializeCEF := TCEFManager.Create; try - if FInitializeCEF.Start then - begin - InitThemeLibrary; - SetThemeAppProperties(STAP_ALLOW_NONCLIENT or STAP_ALLOW_CONTROLS or STAP_ALLOW_WEBCONTENT); - Application.MainFormOnTaskBar := True; - Application.Initialize; - // TStyleManager.TrySetStyle(FKeymanDeveloperOptions.DisplayTheme); - Application.Title := 'Keyman Developer'; - //TBX.TBXSetTheme('OfficeXP2'); - if TikeActive then Exit; - InitClasses; - Application.CreateForm(TmodWebHttpServer, modWebHttpServer); - Application.CreateForm(TfrmKeymanDeveloper, frmKeymanDeveloper); - Application.Run; + CoInitFlags := COINIT_APARTMENTTHREADED; + + FInitializeCEF := TCEFManager.Create; + try + if FInitializeCEF.Start then + begin + InitThemeLibrary; + SetThemeAppProperties(STAP_ALLOW_NONCLIENT or STAP_ALLOW_CONTROLS or STAP_ALLOW_WEBCONTENT); + Application.MainFormOnTaskBar := True; + Application.Initialize; + // TStyleManager.TrySetStyle(FKeymanDeveloperOptions.DisplayTheme); + Application.Title := 'Keyman Developer'; + //TBX.TBXSetTheme('OfficeXP2'); + if TikeActive then Exit; + InitClasses; + Application.CreateForm(TmodWebHttpServer, modWebHttpServer); + Application.CreateForm(TfrmKeymanDeveloper, frmKeymanDeveloper); + Application.Run; + end; + finally + FInitializeCEF.Free; end; - finally - FInitializeCEF.Free; + except + on E:Exception do + SentryHandleException(E); end; finally TKeymanSentryClient.Stop; diff --git a/windows/src/developer/inst/kmdev.wxs b/windows/src/developer/inst/kmdev.wxs index 1f9b8b84aa..df48802bb9 100644 --- a/windows/src/developer/inst/kmdev.wxs +++ b/windows/src/developer/inst/kmdev.wxs @@ -166,6 +166,10 @@ + + + + diff --git a/windows/src/global/delphi/general/KeymanPaths.pas b/windows/src/global/delphi/general/KeymanPaths.pas index a209a18079..8a2e611f92 100644 --- a/windows/src/global/delphi/general/KeymanPaths.pas +++ b/windows/src/global/delphi/general/KeymanPaths.pas @@ -13,6 +13,7 @@ type const S_CEF_DebugPath = 'Debug_CEFPath'; const S_CEF_EnvVar = 'KEYMAN_CEF4DELPHI_ROOT'; const S_CEF_SubFolder = 'cef\'; + const S_CEF_LibCef = 'libcef.dll'; const S_CEF_SubProcess = 'kmbrowserhost.exe'; public const S_KMShell = 'kmshell.exe'; @@ -212,36 +213,89 @@ begin end; class function TKeymanPaths.CEFPath: string; -begin - Result := GetDebugPath(S_CEF_DebugPath, ''); - if Result = '' then + + function AppendSlash(const path: string): string; begin - Result := GetEnvironmentVariable(S_CEF_EnvVar); - if Result = '' - then Result := KeymanDesktopInstallPath+S_CEF_SubFolder - else Result := IncludeTrailingPathDelimiter(Result); + Result := path; + if Result <> '' then + Result := IncludeTrailingPathDelimiter(path); end; + + function IsValidPath(const path: string): Boolean; + begin + Result := (path <> '') and FileExists(path + S_CEF_LibCef); + end; + +begin + // cef\ subfolder of executable path + Result := ExtractFilePath(ParamStr(0)) + S_CEF_SubFolder; + if IsValidPath(Result) then + Exit; + + // Debug_CEFPath registry setting + Result := AppendSlash(GetDebugPath(S_CEF_DebugPath, '')); + if IsValidPath(Result) then + Exit; + + // KEYMAN_CEF4DELPHI_ROOT environment variable + Result := AppendSlash(GetEnvironmentVariable(S_CEF_EnvVar)); + if IsValidPath(Result) then + Exit; + + // Same folder as executable + Result := ExtractFilePath(ParamStr(0)); + if IsValidPath(Result) then + Exit; + + // Keyman Desktop installation folder + cef\ + try + Result := KeymanDesktopInstallPath+S_CEF_SubFolder + except + on E:EKeymanPath do + Result := ''; + end; + if IsValidPath(Result) then + Exit; + + // Failed, could not find libcef.dll + Result := ''; end; class function TKeymanPaths.CEFSubprocessPath: string; +var + keyman_root: string; begin - // Normal install location - in Keyman install folder - Result := KeymanDesktopInstallPath(S_CEF_SubProcess); - if FileExists(Result) then Exit; - // Same folder as executable Result := ExtractFilePath(ParamStr(0)) + S_CEF_SubProcess; if FileExists(Result) then Exit; - // Source repo, bin folder - Result := ExtractFilePath(ParamStr(0)) + '..\desktop\' + S_CEF_SubProcess; - if FileExists(Result) then Exit; + // On developer machines, if we are running within the source repo, then use + // those paths + keyman_root := GetEnvironmentVariable('KEYMAN_ROOT'); + if (keyman_root <> '') and SameText(keyman_root, ParamStr(0).Substring(0, keyman_root.Length)) then + begin + // Source repo, bin folder + Result := IncludeTrailingPathDelimiter(keyman_root) + 'windows\bin\desktop\' + S_CEF_SubProcess; + if FileExists(Result) then Exit; - // Source repo, source folder - Result := ExtractFilePath(ParamStr(0)) + '..\..\desktop\kmbrowserhost\win32\debug\' + S_CEF_SubProcess; - if FileExists(Result) then Exit; + // Source repo, source folder + Result := IncludeTrailingPathDelimiter(keyman_root) + 'windows\bin\desktop\kmbrowserhost\win32\debug\' + S_CEF_SubProcess; + if FileExists(Result) then Exit; - Result := ExtractFilePath(ParamStr(0)) + '..\..\desktop\kmbrowserhost\win32\release\' + S_CEF_SubProcess; + Result := IncludeTrailingPathDelimiter(keyman_root) + 'windows\bin\desktop\kmbrowserhost\win32\release\' + S_CEF_SubProcess; + if FileExists(Result) then Exit; + end; + + // Check final install location - in Keyman for Windows install folder + try + Result := KeymanDesktopInstallPath(S_CEF_SubProcess); + except + on E:EKeymanPath do + Result := ''; + end; + if (Result <> '') and FileExists(Result) then Exit; + + Result := ''; end; class function TKeymanPaths.CEFDataPath(const mode: string): string; diff --git a/windows/src/global/delphi/lexicalmodels/Keyman.Developer.System.LexicalModelCompile.pas b/windows/src/global/delphi/lexicalmodels/Keyman.Developer.System.LexicalModelCompile.pas index b2e88eccc1..2e8e2d17cb 100644 --- a/windows/src/global/delphi/lexicalmodels/Keyman.Developer.System.LexicalModelCompile.pas +++ b/windows/src/global/delphi/lexicalmodels/Keyman.Developer.System.LexicalModelCompile.pas @@ -28,7 +28,7 @@ begin cmdline := Format('"%skmlmc.cmd" "%s" -o "%s"', [ExtractFilePath(ParamStr(0)), infile, outfile]); Result := TUtilExecute.Console(cmdline, ExtractFileDir(infile), logtext, ec); - logtext := UTF8Decode(AnsiString(logtext)); + logtext := UTF8ToString(AnsiString(logtext)); if not Result then begin From 9ffc378fef6355b15af8fd757b1a9cff59bbc26c Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Wed, 3 Mar 2021 10:42:35 +1100 Subject: [PATCH 076/125] chore(developer): fixup wxs path --- windows/src/developer/inst/kmdev.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/src/developer/inst/kmdev.wxs b/windows/src/developer/inst/kmdev.wxs index df48802bb9..e6580df231 100644 --- a/windows/src/developer/inst/kmdev.wxs +++ b/windows/src/developer/inst/kmdev.wxs @@ -167,7 +167,7 @@ - + From 290d21f4c629c82ddf5d7616bd4e84d153e1d97d Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Wed, 3 Mar 2021 10:45:52 +1100 Subject: [PATCH 077/125] chore(developer): address review feedback --- .../Keyman.Developer.UI.UfrmModelEditor.dfm | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.dfm b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.dfm index 01c331127e..412ad9540e 100644 --- a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.dfm +++ b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.dfm @@ -83,7 +83,7 @@ inherited frmModelEditor: TfrmModelEditor Top = 200 Width = 415 Height = 13 - Caption = + Caption = 'The editor was unable to parse the source file. Details shown he' + 're are read-only.' end @@ -219,7 +219,7 @@ inherited frmModelEditor: TfrmModelEditor Width = 26 Height = 13 Caption = 'close' - FocusControl = cbOpenQuote + FocusControl = cbCloseQuote end object cbFormat: TComboBox Left = 106 @@ -265,11 +265,6 @@ inherited frmModelEditor: TfrmModelEditor OnChange = cbInsertAfterWordClick OnClick = cbInsertAfterWordClick OnKeyUp = cbInsertAfterWordKeyUp - Items.Strings = ( - '(space U+0020)' - '(no word break)' - '(Tibetan tsheg U+0F0B)' - '(Ethiopian wordspace U+1361)') end object cbOpenQuote: TComboBox Left = 139 @@ -280,10 +275,6 @@ inherited frmModelEditor: TfrmModelEditor OnChange = cbOpenQuoteClick OnClick = cbOpenQuoteClick OnKeyUp = cbOpenQuoteKeyUp - Items.Strings = ( - 'default' - 'ascii' - 'custom') end object chkIsRTL: TCheckBox Left = 106 @@ -303,10 +294,6 @@ inherited frmModelEditor: TfrmModelEditor OnChange = cbCloseQuoteClick OnClick = cbCloseQuoteClick OnKeyUp = cbCloseQuoteKeyUp - Items.Strings = ( - 'default' - 'ascii' - 'custom') end end end @@ -333,7 +320,7 @@ inherited frmModelEditor: TfrmModelEditor Top = 13 Width = 333 Height = 13 - Caption = + Caption = 'The keyboard must be compiled in order to distribute or install ' + 'it' end @@ -410,7 +397,7 @@ inherited frmModelEditor: TfrmModelEditor Top = 72 Width = 389 Height = 26 - Caption = + Caption = 'Optionally, select a compiled keyboard with which to test this l' + 'exical model. Any keyboards already loaded in the web debugger w' + 'ill also be available.' From 399c7fb6b4ab26e7ff1796729615c12f89d256e4 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 3 Mar 2021 08:55:37 +0700 Subject: [PATCH 078/125] fix(web): fixes lack of respect for underlying-key display settings --- .../src/keyboards/defaultLayouts.ts | 11 ++++++++--- .../web/keyboard-processor/src/keyboards/keyboard.ts | 5 ----- web/source/osk/visualKeyboard.ts | 7 ++----- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/common/core/web/keyboard-processor/src/keyboards/defaultLayouts.ts b/common/core/web/keyboard-processor/src/keyboards/defaultLayouts.ts index 859e870bb2..706bfa7c62 100644 --- a/common/core/web/keyboard-processor/src/keyboards/defaultLayouts.ts +++ b/common/core/web/keyboard-processor/src/keyboards/defaultLayouts.ts @@ -37,15 +37,15 @@ namespace com.keyman.keyboards { shiftKey?: LayoutKey, capsKey?: LayoutKey, numKey?: LayoutKey, - scrollKey?: LayoutKey + scrollKey?: LayoutKey, aligned?: boolean } export type LayoutFormFactor = { + "displayUnderlying"?: boolean, "font": string, "layer": LayoutLayer[], - keyLabels?: boolean, - isDefault?: boolean; + isDefault?: boolean } export type LayoutSpec = { @@ -176,6 +176,11 @@ namespace com.keyman.keyboards { if(formFactor == 'desktop') { invalidIdList = Layouts.generateLayerIds(chiral); + // Only display these by default for the desktop layout. + // Mobile form-factors must have a pre-defined layout in order + // to display underlying keys. + layout["displayUnderlying"] = !!keyboard.scriptObject['KDU']; + // Filter out all ids considered valid. (We also don't want duplicates in the following list...) for(n=0; n Date: Wed, 3 Mar 2021 10:17:15 +0700 Subject: [PATCH 079/125] feat(ios): adds a progress bar to search during page loads --- .../KeyboardSearchViewController.swift | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/KeyboardSearchViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/KeyboardSearchViewController.swift index a46a61e59d..1f9e5119e5 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/KeyboardSearchViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/KeyboardSearchViewController.swift @@ -69,6 +69,9 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat private let languageCode: String? private let session: URLSession + private var progressView: UIProgressView? + private var observation: NSKeyValueObservation? = nil + private static var ENDPOINT_ROOT: URL { var baseURL = KeymanHosts.KEYMAN_COM baseURL.appendPathComponent("go") @@ -93,6 +96,10 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat fatalError("init(coder:) has not been implemented") } + deinit { + observation = nil + } + public override func loadView() { let webView = WKWebView() webView.navigationDelegate = self @@ -104,6 +111,18 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat webView.load(URLRequest(url: KeyboardSearchViewController.ENDPOINT_ROOT)) } + progressView = UIProgressView(progressViewStyle: .bar) + progressView!.translatesAutoresizingMaskIntoConstraints = false + observation = webView.observe(\.estimatedProgress) { _, _ in + if let progressView = self.progressView { + progressView.setProgress(Float(webView.estimatedProgress), animated: true) + progressView.isHidden = progressView.progress > 0.99 + } + } + progressView!.setProgress(1, animated: false) + progressView!.isHidden = true + + webView.addSubview(progressView!) view = webView } @@ -131,6 +150,25 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat } decisionHandler(.allow) + // Makes it clear that there IS a progress bar, in case of super-slow response. + progressView?.setProgress(0, animated: false) + // This way, if the load is instant, the 0.01 doesn't really stand out. + progressView?.setProgress(0.01, animated: true) + progressView?.isHidden = false + } + + + override public func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if let navVC = self.navigationController { + progressView?.topAnchor.constraint(equalTo: navVC.navigationBar.bottomAnchor).isActive = true + } else { + progressView?.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true + } + + progressView?.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true + progressView?.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true } override public func viewWillDisappear(_ animated: Bool) { From 49f05e879d5a0459776a97ec01064e3c6e8c4304 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Wed, 3 Mar 2021 14:24:59 +1100 Subject: [PATCH 080/125] fix(developer): Support all fonts in Keyboard Fonts dialog Fixes #2789. The Keyboard Fonts dialog skipped some fonts which it did not recognise as valid TrueType fonts. As a bonus, I've rewritten a few parts of it to make it significantly faster to load. I also turned off the font formatting for the list, as it was not very helpful and hurt performance considerably. --- .../TIKE/dialogs/UfrmKeyboardFonts.dfm | 18 +++ .../src/ext/scfontcombobox/scFontCombobox.pas | 153 ++++++++++++------ 2 files changed, 123 insertions(+), 48 deletions(-) diff --git a/windows/src/developer/TIKE/dialogs/UfrmKeyboardFonts.dfm b/windows/src/developer/TIKE/dialogs/UfrmKeyboardFonts.dfm index 499ed64986..bd8ba4de5b 100644 --- a/windows/src/developer/TIKE/dialogs/UfrmKeyboardFonts.dfm +++ b/windows/src/developer/TIKE/dialogs/UfrmKeyboardFonts.dfm @@ -166,6 +166,9 @@ inherited frmKeyboardFonts: TfrmKeyboardFonts PreviewWidth = 240 PreviewHeight = 60 FontTypes = [ftTrueTypeAnsi, ftTrueTypeSymbol] + ShowPreview = False + ShowPreviewFontName = False + ShowPreviewInList = False end object editCodeSize: TEdit Left = 391 @@ -193,6 +196,9 @@ inherited frmKeyboardFonts: TfrmKeyboardFonts PreviewWidth = 240 PreviewHeight = 60 FontTypes = [ftTrueTypeAnsi, ftTrueTypeSymbol] + ShowPreview = False + ShowPreviewFontName = False + ShowPreviewInList = False end object editCharSize: TEdit Left = 391 @@ -220,6 +226,9 @@ inherited frmKeyboardFonts: TfrmKeyboardFonts PreviewWidth = 240 PreviewHeight = 60 FontTypes = [ftTrueTypeAnsi, ftTrueTypeSymbol] + ShowPreview = False + ShowPreviewFontName = False + ShowPreviewInList = False end object editOSKSize: TEdit Left = 391 @@ -288,6 +297,9 @@ inherited frmKeyboardFonts: TfrmKeyboardFonts PreviewWidth = 240 PreviewHeight = 60 FontTypes = [ftTrueTypeAnsi, ftTrueTypeSymbol] + ShowPreview = False + ShowPreviewFontName = False + ShowPreviewInList = False end object fcbTouchLayoutTablet: TscFontComboBox Left = 176 @@ -308,6 +320,9 @@ inherited frmKeyboardFonts: TfrmKeyboardFonts PreviewWidth = 240 PreviewHeight = 60 FontTypes = [ftTrueTypeAnsi, ftTrueTypeSymbol] + ShowPreview = False + ShowPreviewFontName = False + ShowPreviewInList = False end object fcbTouchLayoutDesktop: TscFontComboBox Left = 176 @@ -328,5 +343,8 @@ inherited frmKeyboardFonts: TfrmKeyboardFonts PreviewWidth = 240 PreviewHeight = 60 FontTypes = [ftTrueTypeAnsi, ftTrueTypeSymbol] + ShowPreview = False + ShowPreviewFontName = False + ShowPreviewInList = False end end diff --git a/windows/src/ext/scfontcombobox/scFontCombobox.pas b/windows/src/ext/scfontcombobox/scFontCombobox.pas index d7cb453cec..8a73899896 100644 --- a/windows/src/ext/scfontcombobox/scFontCombobox.pas +++ b/windows/src/ext/scfontcombobox/scFontCombobox.pas @@ -60,6 +60,18 @@ type TscFontType = (ftTrueTypeAnsi, ftTrueTypeSymbol, ftRaster); TscFontTypes = set of TscFontType; + TscFontRefreshHandler = class + private + FFontNames: TStrings; + FRefreshWnd: THandle; + procedure RefreshWndProc(var Message: TMessage); + procedure ReadFontNames; + public + constructor Create; + destructor Destroy; override; + property FontNames: TStrings read FFontNames; + end; + TscFontComboBox = class(TCustomComboBox) private BitmapTrueTypeAnsi : TBitmap; @@ -82,6 +94,8 @@ type FBlnShowPreviewFontName: Boolean; FBlnShowPreviewInEdit: Boolean; + class var FontRefreshHandler: TscFontRefreshHandler; + procedure SetPopupHeight(const Value: Integer); procedure SetPopupWidth(const Value: Integer); procedure SetFontName(const Value: String); @@ -183,6 +197,11 @@ implementation var FormExample : TFormExampleFont = nil; +const + FTV_UNKNOWN = 0; + FTV_ANSI = 1; + FTV_SYMBOL = 2; + FTV_RASTER = 3; procedure Register; begin @@ -234,6 +253,8 @@ begin IntCountUsed := 0; BlnDown := False; + Sorted := True; + Style := csOwnerDrawFixed; ItemHeight := 18; DropDownCount := 12; @@ -270,7 +291,7 @@ begin with Canvas do begin StrFont := Items.Names[Index]; - StrFontType := Items.Values[StrFont]; + StrFontType := Items.ValueFromIndex[Index]; FillRect(Rect); @@ -458,58 +479,26 @@ begin end; procedure TscFontComboBox.GetFontNames; -type - TFontRec = record - FontTypes : TscFontTypes; - FontItems : TStrings; - end; var - DC: HDC; - FontRec : TFontRec; - - function EnumFontsProc(var LogFont: TLogFont; var TextMetric: TTextMetric; - FontType: Integer; Data: Pointer): Integer; stdcall; - var - StrType : String; - begin - StrType :=''; - if FontType = TRUETYPE_FONTTYPE then - case LogFont.lfCharset of - ANSI_CHARSET : StrType := 'TA'; - SYMBOL_CHARSET : StrType := 'TS'; - end - else - if FontType = RASTER_FONTTYPE then - StrType := 'R'; - - // Check which fonts have to be added to listbox - if (LogFont.lfFaceName[0] <> '@') - and - (((StrType = 'TA') and (ftTrueTypeAnsi in TFontRec(Data^).FontTypes)) or - ((StrType = 'TS') and (ftTrueTypeSymbol in TFontRec(Data^).FontTypes)) or - ((StrType = 'R') and (ftRaster in TFontRec(Data^).FontTypes))) then - begin - TStrings(TFontRec(Data^).FontItems).Add(string(LogFont.lfFaceName)+'='+StrType); - end; - - Result := 1; - end; + i: Integer; begin - IntCountUsed := 0; - Items.Clear; - - // Make record to provide a pointer to the items - // of the listbox and the property fonttypes - FontRec.FontItems := Pointer(Items); - FontRec.FontTypes := FFontTypes; - - DC := GetDC(0); + Items.BeginUpdate; try - EnumFonts(DC, nil, @EnumFontsProc, @FontRec); + Items.Clear; + if not Assigned(FontRefreshHandler) then + FontRefreshHandler := TscFontRefreshHandler.Create; + for i := 0 to FontRefreshHandler.FontNames.Count - 1 do + begin + case Integer(FontRefreshHandler.FontNames.Objects[i]) of + FTV_UNKNOWN, //#2789 support all charsets + FTV_ANSI: Items.Add(FontRefreshHandler.FontNames[i]+'=TA'); + FTV_SYMBOL: Items.Add(FontRefreshHandler.FontNames[i]+'=TS'); + FTV_RASTER: Items.Add(FontRefreshHandler.FontNames[i]+'=R'); + end; + end; finally - ReleaseDC(0, DC); + Items.EndUpdate; end; - Sorted := True; end; //------------------------------------------------------------------------------ @@ -556,8 +545,76 @@ begin FormExample.PanelFontName.Visible := FBlnShowPreviewFontName; end; +{ TscFontRefreshHandler } + +constructor TscFontRefreshHandler.Create; +begin + inherited Create; + FFontNames := TStringList.Create(dupIgnore, True, False); + FRefreshWnd := AllocateHWnd(RefreshWndProc); + ReadFontNames; +end; + +destructor TscFontRefreshHandler.Destroy; +begin + FFontNames.Free; + DeallocateHWnd(FRefreshWnd); + inherited Destroy; +end; + +function EnumFontFamExProc(var LogFont: TLogFont; var TextMetric: TTextMetric; + FontType: DWORD; Data: LPARAM): Integer; stdcall; +var + FontTypeValue : Integer; +begin + FontTypeValue := FTV_UNKNOWN; + if FontType = TRUETYPE_FONTTYPE then + case LogFont.lfCharset of + ANSI_CHARSET : FontTypeValue := FTV_ANSI; //'TA' + SYMBOL_CHARSET : FontTypeValue := FTV_SYMBOL; //'TS' + else FontTypeValue := FTV_ANSI; //'TA'; #2789 support all charsets + end + else if FontType = RASTER_FONTTYPE then + FontTypeValue := FTV_RASTER; //'R' + + TscFontRefreshHandler(Data).FFontNames.AddObject(LogFont.lfFaceName, Pointer(FontTypeValue)); + Result := 1; +end; + +procedure TscFontRefreshHandler.ReadFontNames; +var + DC: HDC; + lf: TLogFont; +begin + FFontNames.Clear; + + FillChar(lf, sizeof(TLogFont), 0); + lf.lfCharSet := DEFAULT_CHARSET; + + DC := GetDC(0); + try + EnumFontFamiliesEx(DC, lf, @EnumFontFamExProc, NativeInt(Self), 0); + finally + ReleaseDC(0, DC); + end; +end; + +procedure TscFontRefreshHandler.RefreshWndProc(var Message: TMessage); +begin + try + if Message.Msg = WM_FONTCHANGE then + begin + ReadFontNames; + end; + Message.Result := DefWindowProc(FRefreshWnd, Message.Msg, Message.WParam, Message.LParam); + except + on E:Exception do Application.HandleException(E); + end; +end; + initialization finalization FreeAndNil(FormExample); + FreeAndNil(TscFontComboBox.FontRefreshHandler); end. From c595767e395bf6fbfec87cd36f3410f4a0889e0d Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Wed, 3 Mar 2021 15:36:11 +1100 Subject: [PATCH 081/125] feat(developer): Add different Open Containing Folder buttons Fixes #4314. Adds 'Open Source Folder', 'Open Build Folder', and 'Open Project Folder' actions to various parts of Keyman Developer. --- .../Keyman.Developer.UI.UfrmModelEditor.dfm | 152 +++++++++---- .../Keyman.Developer.UI.UfrmModelEditor.pas | 43 +++- .../developer/TIKE/child/UfrmKeymanWizard.dfm | 181 +++++++++------- .../developer/TIKE/child/UfrmKeymanWizard.pas | 33 ++- .../TIKE/child/UfrmPackageEditor.dfm | 203 ++++++++++++------ .../TIKE/child/UfrmPackageEditor.pas | 65 ++++-- ...eyman.Developer.UI.Project.UfrmProject.pas | 14 ++ .../developer/TIKE/xml/project/keyboards.xsl | 7 +- .../src/developer/TIKE/xml/project/models.xsl | 7 +- .../developer/TIKE/xml/project/packages.xsl | 7 +- .../developer/TIKE/xml/project/welcome.xsl | 5 + .../src/global/delphi/general/utilsystem.pas | 6 +- 12 files changed, 492 insertions(+), 231 deletions(-) diff --git a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.dfm b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.dfm index 412ad9540e..d07f24d186 100644 --- a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.dfm +++ b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.dfm @@ -6,7 +6,6 @@ inherited frmModelEditor: TfrmModelEditor ClientWidth = 712 Font.Name = 'Tahoma' Position = poDesigned - ExplicitTop = -18 ExplicitWidth = 712 ExplicitHeight = 708 PixelsPerInch = 96 @@ -60,7 +59,6 @@ inherited frmModelEditor: TfrmModelEditor Align = alTop BevelOuter = bvNone TabOrder = 1 - ExplicitTop = 181 DesignSize = ( 611 232) @@ -324,47 +322,15 @@ inherited frmModelEditor: TfrmModelEditor 'The keyboard must be compiled in order to distribute or install ' + 'it' end - object Label5: TLabel - Left = 10 - Top = 79 - Width = 82 - Height = 13 - Caption = 'Target filename:' - end - object cmdCompile: TButton - Left = 10 - Top = 40 - Width = 137 - Height = 25 - Action = modActionsModelEditor.actModelCompile - TabOrder = 0 - end - object cmdAddToProject: TButton - Left = 297 - Top = 40 - Width = 137 - Height = 25 - Action = modActionsMain.actProjectAddCurrentEditorFile - TabOrder = 1 - end - object cmdOpenContainingFolder2: TButton - Left = 153 - Top = 40 - Width = 138 - Height = 25 - Caption = '&Open Containing Folder' - TabOrder = 2 - OnClick = cmdOpenContainingFolder2Click - end object panBuildLexicalModel: TPanel Left = 10 - Top = 112 + Top = 235 Width = 471 Height = 353 BevelOuter = bvNone Color = 15921906 ParentBackground = False - TabOrder = 3 + TabOrder = 2 object lblDebugHostCaption: TLabel Left = 12 Top = 158 @@ -464,15 +430,111 @@ inherited frmModelEditor: TfrmModelEditor OnClick = cmdBrowseTestKeyboardClick end end - object editOutPath: TEdit - Left = 98 - Top = 76 - Width = 383 - Height = 21 - TabStop = False - ParentColor = True - ReadOnly = True - TabOrder = 4 + object panOpenInExplorer: TPanel + Left = 10 + Top = 148 + Width = 471 + Height = 67 + BevelOuter = bvNone + Color = 15921906 + ParentBackground = False + TabOrder = 1 + object lblOpenInExplorer: TLabel + Left = 9 + Top = 6 + Width = 115 + Height = 17 + Caption = 'Open in Explorer' + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -14 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + end + object cmdOpenSourceFolder: TButton + Left = 9 + Top = 33 + Width = 138 + Height = 25 + Caption = '&Open source folder' + TabOrder = 0 + OnClick = cmdOpenSourceFolderClick + end + object cmdOpenBuildFolder: TButton + Left = 153 + Top = 33 + Width = 138 + Height = 25 + Caption = 'Open &build folder' + TabOrder = 1 + OnClick = cmdOpenBuildFolderClick + end + object cmdOpenProjectFolder: TButton + Left = 297 + Top = 33 + Width = 138 + Height = 25 + Caption = 'Open pro&ject folder' + TabOrder = 2 + OnClick = cmdOpenProjectFolderClick + end + end + object panFileActions: TPanel + Left = 10 + Top = 32 + Width = 471 + Height = 97 + BevelOuter = bvNone + Color = 15921906 + ParentBackground = False + TabOrder = 0 + object lblFileActions: TLabel + Left = 9 + Top = 6 + Width = 75 + Height = 17 + Caption = 'File actions' + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -14 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + end + object Label5: TLabel + Left = 10 + Top = 71 + Width = 82 + Height = 13 + Caption = 'Target filename:' + end + object cmdAddToProject: TButton + Left = 153 + Top = 32 + Width = 137 + Height = 25 + Action = modActionsMain.actProjectAddCurrentEditorFile + TabOrder = 1 + end + object cmdCompile: TButton + Left = 10 + Top = 32 + Width = 137 + Height = 25 + Action = modActionsModelEditor.actModelCompile + TabOrder = 0 + end + object editOutPath: TEdit + Left = 98 + Top = 68 + Width = 359 + Height = 21 + TabStop = False + ParentColor = True + ReadOnly = True + TabOrder = 2 + end end end end diff --git a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas index de4e2bf984..565d3f5a0d 100644 --- a/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas +++ b/windows/src/developer/TIKE/child/Keyman.Developer.UI.UfrmModelEditor.pas @@ -49,9 +49,6 @@ type pageCompile: TTabSheet; Panel1: TPanel; lblCongrats: TLabel; - cmdCompile: TButton; - cmdAddToProject: TButton; - cmdOpenContainingFolder2: TButton; panBuildLexicalModel: TPanel; cbFormat: TComboBox; cbWordBreaker: TComboBox; @@ -71,8 +68,6 @@ type lblReadOnly: TLabel; dlgAddWordlist: TOpenDialog; dlgBrowseTestKeyboard: TOpenDialog; - Label5: TLabel; - editOutPath: TEdit; imgQRCode: TImage; lblInsertAfterWord: TLabel; cbInsertAfterWord: TComboBox; @@ -82,6 +77,17 @@ type cbCloseQuote: TComboBox; lblOpenQuote: TLabel; lblCloseQuote: TLabel; + panOpenInExplorer: TPanel; + lblOpenInExplorer: TLabel; + cmdOpenSourceFolder: TButton; + cmdOpenBuildFolder: TButton; + cmdOpenProjectFolder: TButton; + panFileActions: TPanel; + lblFileActions: TLabel; + cmdAddToProject: TButton; + cmdCompile: TButton; + Label5: TLabel; + editOutPath: TEdit; procedure FormDestroy(Sender: TObject); procedure cmdAddWordlistClick(Sender: TObject); procedure cmdRemoveWordlistClick(Sender: TObject); @@ -91,7 +97,6 @@ type procedure cbFormatClick(Sender: TObject); procedure cbWordBreakerClick(Sender: TObject); procedure memoCommentsChange(Sender: TObject); - procedure cmdOpenContainingFolder2Click(Sender: TObject); procedure cmdOpenDebugHostClick(Sender: TObject); procedure cmdSendURLsToEmailClick(Sender: TObject); procedure cmdBrowseTestKeyboardClick(Sender: TObject); @@ -107,6 +112,9 @@ type procedure cbCloseQuoteKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); procedure chkIsRTLClick(Sender: TObject); + procedure cmdOpenSourceFolderClick(Sender: TObject); + procedure cmdOpenBuildFolderClick(Sender: TObject); + procedure cmdOpenProjectFolderClick(Sender: TObject); private type TWordlist = class @@ -395,6 +403,12 @@ begin { Build tab } cmdOpenDebugHost.Enabled := lbDebugHosts.ItemIndex >= 0; cmdSendURLsToEmail.Enabled := lbDebugHosts.Items.Count > 0; // I4506 + + // We use FProjectFile because we don't want to accidentally create a standalone + // project file as GetProjectFile is side-effecty. EnableControls is called early + // in construction before FProjectFile is assigned. It is called again later so + // enabled state will be correct. + cmdOpenProjectFolder.Enabled := Assigned(FProjectFile) and Assigned(FProjectFile.Project); end; procedure TfrmModelEditor.NotifyStartedWebDebug; @@ -783,14 +797,25 @@ begin editTestKeyboard.Text := dlgBrowseTestKeyboard.FileName; end; -procedure TfrmModelEditor.cmdOpenContainingFolder2Click(Sender: TObject); +procedure TfrmModelEditor.cmdOpenDebugHostClick(Sender: TObject); +begin + TUtilExecute.URL(lbDebugHosts.Items[lbDebugHosts.ItemIndex]); +end; + +procedure TfrmModelEditor.cmdOpenSourceFolderClick(Sender: TObject); begin OpenContainingFolder(FileName); end; -procedure TfrmModelEditor.cmdOpenDebugHostClick(Sender: TObject); +procedure TfrmModelEditor.cmdOpenBuildFolderClick(Sender: TObject); begin - TUtilExecute.URL(lbDebugHosts.Items[lbDebugHosts.ItemIndex]); + OpenContainingFolder((ProjectFile as TmodeltsProjectFile).TargetFilename); +end; + +procedure TfrmModelEditor.cmdOpenProjectFolderClick(Sender: TObject); +begin + if Assigned(ProjectFile.Project) then + OpenContainingFolder(ProjectFile.Project.FileName); end; procedure TfrmModelEditor.cmdRemoveWordlistClick(Sender: TObject); diff --git a/windows/src/developer/TIKE/child/UfrmKeymanWizard.dfm b/windows/src/developer/TIKE/child/UfrmKeymanWizard.dfm index d785ba874a..b8cb54c560 100644 --- a/windows/src/developer/TIKE/child/UfrmKeymanWizard.dfm +++ b/windows/src/developer/TIKE/child/UfrmKeymanWizard.dfm @@ -177,7 +177,7 @@ inherited frmKeymanWizard: TfrmKeymanWizard Top = 0 Width = 1043 Height = 645 - ActivePage = pageTouchLayout + ActivePage = pageDetails Align = alClient Font.Charset = ANSI_CHARSET Font.Color = clWindowText @@ -698,7 +698,7 @@ inherited frmKeymanWizard: TfrmKeymanWizard Top = 22 Width = 482 Height = 13 - Caption = + Caption = 'In Keyman 10, language metadata should now be managed in the pac' + 'kage, not the keyboard.' end @@ -721,9 +721,6 @@ inherited frmKeymanWizard: TfrmKeymanWizard object pageLayout: TTabSheet Caption = 'Layout' ImageIndex = 5 - ExplicitLeft = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 object pagesLayout: TPageControl Left = 0 Top = 0 @@ -738,10 +735,6 @@ inherited frmKeymanWizard: TfrmKeymanWizard object pageLayoutDesign: TTabSheet Caption = 'Design' ImageIndex = -1 - ExplicitLeft = 0 - ExplicitTop = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 object panLayoutSimple: TPanel Left = 0 Top = 0 @@ -965,10 +958,6 @@ inherited frmKeymanWizard: TfrmKeymanWizard object pageLayoutCode: TTabSheet Caption = 'Code' ImageIndex = -1 - ExplicitLeft = 0 - ExplicitTop = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 end end end @@ -1015,9 +1004,6 @@ inherited frmKeymanWizard: TfrmKeymanWizard object pageOnScreenKeyboard: TTabSheet Caption = 'On-Screen' ImageIndex = 7 - ExplicitLeft = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 end object pageTouchLayout: TTabSheet Caption = 'Touch Layout' @@ -1040,47 +1026,28 @@ inherited frmKeymanWizard: TfrmKeymanWizard object pageTouchLayoutCode: TTabSheet Caption = 'Code' ImageIndex = -1 - ExplicitLeft = 0 - ExplicitTop = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 end end end object pageIncludeCodes: TTabSheet Caption = 'Char Codes' ImageIndex = 9 - ExplicitLeft = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 end object pageKMWEmbedJS: TTabSheet Caption = 'Embedded JS' ImageIndex = 9 - ExplicitLeft = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 end object pageKMWEmbedCSS: TTabSheet Caption = 'Embedded CSS' ImageIndex = 9 - ExplicitLeft = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 end object pageKMWHelp: TTabSheet Caption = 'Embedded Help' ImageIndex = 9 - ExplicitLeft = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 end object pageCompile: TTabSheet Caption = 'Build' ImageIndex = 1 - ExplicitLeft = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 object Panel1: TPanel Left = 0 Top = 0 @@ -1096,52 +1063,19 @@ inherited frmKeymanWizard: TfrmKeymanWizard Top = 13 Width = 333 Height = 13 - Caption = + Caption = 'The keyboard must be compiled in order to distribute or install ' + 'it' end - object cmdCompile: TButton - Left = 10 - Top = 40 - Width = 137 - Height = 25 - Action = modActionsKeyboardEditor.actKeyboardCompile - TabOrder = 0 - end - object cmdStartDebugging: TButton - Left = 153 - Top = 40 - Width = 137 - Height = 25 - Action = modActionsKeyboardEditor.actDebugStartDebugger - TabOrder = 1 - end - object cmdAddToProject: TButton - Left = 442 - Top = 40 - Width = 137 - Height = 25 - Action = modActionsMain.actProjectAddCurrentEditorFile - TabOrder = 2 - end - object cmdOpenContainingFolder2: TButton - Left = 298 - Top = 40 - Width = 138 - Height = 25 - Caption = '&Open Containing Folder' - TabOrder = 3 - OnClick = cmdOpenContainingFolder2Click - end object panBuildWindows: TPanel Left = 10 - Top = 96 + Top = 202 Width = 295 Height = 265 BevelOuter = bvNone Color = 15921906 ParentBackground = False - TabOrder = 4 + TabOrder = 2 object lblInstallHint: TLabel Left = 9 Top = 39 @@ -1189,13 +1123,13 @@ inherited frmKeymanWizard: TfrmKeymanWizard end object panBuildKMW: TPanel Left = 323 - Top = 96 + Top = 202 Width = 454 Height = 265 BevelOuter = bvNone Color = 15921906 ParentBackground = False - TabOrder = 5 + TabOrder = 3 object lblDebugHostCaption: TLabel Left = 12 Top = 70 @@ -1260,6 +1194,103 @@ inherited frmKeymanWizard: TfrmKeymanWizard OnClick = cmdSendURLsToEmailClick end end + object panOpenInExplorer: TPanel + Left = 10 + Top = 117 + Width = 767 + Height = 67 + BevelOuter = bvNone + Color = 15921906 + ParentBackground = False + TabOrder = 1 + object lblOpenInExplorer: TLabel + Left = 9 + Top = 6 + Width = 115 + Height = 17 + Caption = 'Open in Explorer' + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -14 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + end + object cmdOpenSourceFolder: TButton + Left = 9 + Top = 33 + Width = 138 + Height = 25 + Caption = '&Open source folder' + TabOrder = 0 + OnClick = cmdOpenSourceFolderClick + end + object cmdOpenBuildFolder: TButton + Left = 153 + Top = 33 + Width = 138 + Height = 25 + Caption = 'Open &build folder' + TabOrder = 1 + OnClick = cmdOpenBuildFolderClick + end + object cmdOpenProjectFolder: TButton + Left = 297 + Top = 33 + Width = 138 + Height = 25 + Caption = 'Open pro&ject folder' + TabOrder = 2 + OnClick = cmdOpenProjectFolderClick + end + end + object panFileActions: TPanel + Left = 10 + Top = 32 + Width = 767 + Height = 67 + BevelOuter = bvNone + Color = 15921906 + ParentBackground = False + TabOrder = 0 + object lblFileActions: TLabel + Left = 9 + Top = 6 + Width = 75 + Height = 17 + Caption = 'File actions' + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -14 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + end + object cmdAddToProject: TButton + Left = 295 + Top = 33 + Width = 137 + Height = 25 + Action = modActionsMain.actProjectAddCurrentEditorFile + TabOrder = 2 + end + object cmdStartDebugging: TButton + Left = 152 + Top = 33 + Width = 137 + Height = 25 + Action = modActionsKeyboardEditor.actDebugStartDebugger + TabOrder = 1 + end + object cmdCompile: TButton + Left = 9 + Top = 33 + Width = 137 + Height = 25 + Action = modActionsKeyboardEditor.actKeyboardCompile + TabOrder = 0 + end + end end end end @@ -1300,7 +1331,7 @@ inherited frmKeymanWizard: TfrmKeymanWizard end object dlgBrowseBitmap: TOpenPictureDialog DefaultExt = 'ico' - Filter = + Filter = 'All supported files (*.ico, *.bmp)|*.ico;*.bmp|Icon files (*.ico' + ')|*.ico|Bitmap files (*.bmp)|*.bmp|All files (*.*)|*.*' Options = [ofHideReadOnly, ofCreatePrompt, ofEnableSizing] @@ -1310,7 +1341,7 @@ inherited frmKeymanWizard: TfrmKeymanWizard end object dlgSaveExport: TSaveDialog DefaultExt = 'kmn' - Filter = + Filter = 'Keyman 5.0 Keyboard Wizard (*.kmn)|*.kmn|Windows NT/2000/XP keyb' + 'oard (*.dll)|*.dll|Windows 95/98/Me keyboard (*.kbd)|*.kbd' Options = [ofOverwritePrompt, ofHideReadOnly, ofPathMustExist, ofEnableSizing] diff --git a/windows/src/developer/TIKE/child/UfrmKeymanWizard.pas b/windows/src/developer/TIKE/child/UfrmKeymanWizard.pas index 5b9ab1d1c0..4b9041a29e 100644 --- a/windows/src/developer/TIKE/child/UfrmKeymanWizard.pas +++ b/windows/src/developer/TIKE/child/UfrmKeymanWizard.pas @@ -236,10 +236,6 @@ type cmdISOLang_Lookup: TButton; Panel1: TPanel; lblCongrats: TLabel; - cmdCompile: TButton; - cmdStartDebugging: TButton; - cmdAddToProject: TButton; - cmdOpenContainingFolder2: TButton; panBuildWindows: TPanel; lblInstallHint: TLabel; lblCompileTargetHeader: TLabel; @@ -267,6 +263,16 @@ type lblLanguageKeyman10Note: TLabel; lblLanguageKeyman10Title: TLabel; imgQRCode: TImage; + panOpenInExplorer: TPanel; + lblOpenInExplorer: TLabel; + cmdOpenSourceFolder: TButton; + cmdOpenBuildFolder: TButton; + cmdOpenProjectFolder: TButton; + panFileActions: TPanel; + lblFileActions: TLabel; + cmdAddToProject: TButton; + cmdStartDebugging: TButton; + cmdCompile: TButton; procedure FormCreate(Sender: TObject); procedure editNameChange(Sender: TObject); procedure editCopyrightChange(Sender: TObject); @@ -294,7 +300,7 @@ type Shift: TShiftState); procedure chkLayoutDisplay102KeyClick(Sender: TObject); procedure cmdInsertCopyrightClick(Sender: TObject); - procedure cmdOpenContainingFolder2Click(Sender: TObject); + procedure cmdOpenSourceFolderClick(Sender: TObject); procedure editKeyOutputCodeClick(Sender: TObject); procedure editKeyOutputTextClick(Sender: TObject); procedure tmrUpdateCharacterMapTimer(Sender: TObject); @@ -332,6 +338,8 @@ type procedure pagesTouchLayoutChanging(Sender: TObject; var AllowChange: Boolean); procedure lbDebugHostsClick(Sender: TObject); + procedure cmdOpenBuildFolderClick(Sender: TObject); + procedure cmdOpenProjectFolderClick(Sender: TObject); private frameSource: TframeTextEditor; @@ -738,6 +746,8 @@ begin gridFeatures.Enabled := gridFeatures.RowCount > 1; // I4587 // I4427 cmdEditFeature.Enabled := gridFeatures.RowCount > 1; // I4587 // I4427 cmdRemoveFeature.Enabled := gridFeatures.RowCount > 1; // I4587 // I4427 + + cmdOpenProjectFolder.Enabled := Assigned(ProjectFile.Project); end; procedure TfrmKeymanWizard.FocusTab; @@ -2754,11 +2764,22 @@ begin editCopyright.SetFocus; end; -procedure TfrmKeymanWizard.cmdOpenContainingFolder2Click(Sender: TObject); +procedure TfrmKeymanWizard.cmdOpenSourceFolderClick(Sender: TObject); begin OpenContainingFolder(FileName); end; +procedure TfrmKeymanWizard.cmdOpenBuildFolderClick(Sender: TObject); +begin + OpenContainingFolder((ProjectFile as TkmnProjectFile).TargetFilename); +end; + +procedure TfrmKeymanWizard.cmdOpenProjectFolderClick(Sender: TObject); +begin + if Assigned(ProjectFile.Project) then + OpenContainingFolder(ProjectFile.Project.FileName); +end; + procedure TfrmKeymanWizard.cmdOpenDebugHostClick(Sender: TObject); begin TUtilExecute.URL(lbDebugHosts.Items[lbDebugHosts.ItemIndex]); diff --git a/windows/src/developer/TIKE/child/UfrmPackageEditor.dfm b/windows/src/developer/TIKE/child/UfrmPackageEditor.dfm index e3129b35ce..36134ab3ff 100644 --- a/windows/src/developer/TIKE/child/UfrmPackageEditor.dfm +++ b/windows/src/developer/TIKE/child/UfrmPackageEditor.dfm @@ -1119,9 +1119,6 @@ inherited frmPackageEditor: TfrmPackageEditor object pageSource: TTabSheet Caption = 'Source' ImageIndex = 9 - ExplicitLeft = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 end object pageCompile: TTabSheet Caption = 'Compile' @@ -1158,67 +1155,15 @@ inherited frmPackageEditor: TfrmPackageEditor Font.Style = [fsBold] ParentFont = False end - object Label5: TLabel - Left = 24 - Top = 121 - Width = 82 - Height = 13 - Caption = 'Target filename:' - end - object cmdBuildPackage: TButton - Left = 15 - Top = 80 - Width = 133 - Height = 25 - Caption = 'Compile &Package' - TabOrder = 0 - OnClick = cmdBuildPackageClick - end - object editOutPath: TEdit - Left = 111 - Top = 118 - Width = 383 - Height = 21 - TabStop = False - ParentColor = True - ReadOnly = True - TabOrder = 4 - end - object cmdOpenContainingFolder2: TButton - Left = 293 - Top = 80 - Width = 133 - Height = 25 - Caption = '&Open Containing Folder' - TabOrder = 2 - OnClick = cmdOpenContainingFolder2Click - end - object cmdAddToProject: TButton - Left = 432 - Top = 80 - Width = 133 - Height = 25 - Action = modActionsMain.actProjectAddCurrentEditorFile - TabOrder = 3 - end - object cmdCompileInstaller: TButton - Left = 154 - Top = 80 - Width = 133 - Height = 25 - Caption = 'Compile I&nstaller' - TabOrder = 1 - OnClick = cmdCompileInstallerClick - end object panBuildMobile: TPanel Left = 328 - Top = 162 + Top = 274 Width = 457 - Height = 265 + Height = 276 BevelOuter = bvNone Color = 15921906 ParentBackground = False - TabOrder = 5 + TabOrder = 4 object lblDebugHostCaption: TLabel Left = 12 Top = 70 @@ -1241,7 +1186,7 @@ inherited frmPackageEditor: TfrmPackageEditor end object imgQRCode: TImage Left = 315 - Top = 64 + Top = 96 Width = 128 Height = 128 Proportional = True @@ -1258,7 +1203,7 @@ inherited frmPackageEditor: TfrmPackageEditor end object cmdOpenDebugHost: TButton Left = 12 - Top = 198 + Top = 240 Width = 221 Height = 25 Caption = 'Open packages page in local &browser' @@ -1267,9 +1212,9 @@ inherited frmPackageEditor: TfrmPackageEditor end object lbDebugHosts: TListBox Left = 12 - Top = 95 + Top = 89 Width = 289 - Height = 97 + Height = 142 ItemHeight = 13 TabOrder = 1 OnClick = lbDebugHostsClick @@ -1277,26 +1222,26 @@ inherited frmPackageEditor: TfrmPackageEditor end object panBuildDesktop: TPanel Left = 15 - Top = 162 + Top = 274 Width = 295 Height = 124 BevelOuter = bvNone Color = 15921906 ParentBackground = False - TabOrder = 6 + TabOrder = 2 object Label4: TLabel Left = 9 Top = 35 - Width = 254 + Width = 236 Height = 13 - Caption = 'You can install the package into Keyman:' + Caption = 'Install the package into Keyman for Windows:' end object lblCompileTargetHeader: TLabel Left = 9 Top = 6 - Width = 202 + Width = 247 Height = 17 - Caption = 'Windows and macOS Targets' + Caption = 'Windows, Linux and macOS Targets' Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -14 @@ -1327,13 +1272,13 @@ inherited frmPackageEditor: TfrmPackageEditor end object panBuildWindowsInstaller: TPanel Left = 15 - Top = 295 + Top = 418 Width = 295 Height = 132 BevelOuter = bvNone Color = 15921906 ParentBackground = False - TabOrder = 7 + TabOrder = 3 object Label9: TLabel Left = 9 Top = 6 @@ -1350,7 +1295,7 @@ inherited frmPackageEditor: TfrmPackageEditor object lblBootstrapMSI: TLabel Left = 9 Top = 69 - Width = 110 + Width = 64 Height = 13 Caption = 'Keyman MSI:' FocusControl = editBootstrapMSI @@ -1393,6 +1338,122 @@ inherited frmPackageEditor: TfrmPackageEditor OnClick = cmdInstallWithClick end end + object panOpenInExplorer: TPanel + Left = 15 + Top = 187 + Width = 767 + Height = 67 + BevelOuter = bvNone + Color = 15921906 + ParentBackground = False + TabOrder = 1 + object lblOpenInExplorer: TLabel + Left = 9 + Top = 6 + Width = 115 + Height = 17 + Caption = 'Open in Explorer' + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -14 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + end + object cmdOpenSourceFolder: TButton + Left = 9 + Top = 33 + Width = 138 + Height = 25 + Caption = '&Open source folder' + TabOrder = 0 + OnClick = cmdOpenSourceFolderClick + end + object cmdOpenBuildFolder: TButton + Left = 153 + Top = 33 + Width = 138 + Height = 25 + Caption = 'Open &build folder' + TabOrder = 1 + OnClick = cmdOpenBuildFolderClick + end + object cmdOpenProjectFolder: TButton + Left = 297 + Top = 33 + Width = 138 + Height = 25 + Caption = 'Open pro&ject folder' + TabOrder = 2 + OnClick = cmdOpenProjectFolderClick + end + end + object panFileActions: TPanel + Left = 15 + Top = 67 + Width = 767 + Height = 102 + BevelOuter = bvNone + Color = 15921906 + ParentBackground = False + TabOrder = 0 + object lblFileActions: TLabel + Left = 9 + Top = 6 + Width = 75 + Height = 17 + Caption = 'File actions' + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -14 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + end + object Label5: TLabel + Left = 9 + Top = 73 + Width = 82 + Height = 13 + Caption = 'Target filename:' + end + object editOutPath: TEdit + Left = 96 + Top = 70 + Width = 383 + Height = 21 + TabStop = False + ParentColor = True + ReadOnly = True + TabOrder = 0 + end + object cmdCompileInstaller: TButton + Left = 148 + Top = 32 + Width = 133 + Height = 25 + Caption = 'Compile I&nstaller' + TabOrder = 1 + OnClick = cmdCompileInstallerClick + end + object cmdAddToProject: TButton + Left = 287 + Top = 32 + Width = 133 + Height = 25 + Action = modActionsMain.actProjectAddCurrentEditorFile + TabOrder = 2 + end + object cmdBuildPackage: TButton + Left = 9 + Top = 32 + Width = 133 + Height = 25 + Caption = 'Compile &Package' + TabOrder = 3 + OnClick = cmdBuildPackageClick + end + end end end end diff --git a/windows/src/developer/TIKE/child/UfrmPackageEditor.pas b/windows/src/developer/TIKE/child/UfrmPackageEditor.pas index ffdd6c8e49..0889260205 100644 --- a/windows/src/developer/TIKE/child/UfrmPackageEditor.pas +++ b/windows/src/developer/TIKE/child/UfrmPackageEditor.pas @@ -135,11 +135,6 @@ type Panel4: TPanel; Label13: TLabel; lblCompilePackage: TLabel; - cmdBuildPackage: TButton; - editOutPath: TEdit; - cmdOpenContainingFolder2: TButton; - cmdAddToProject: TButton; - cmdCompileInstaller: TButton; pageKeyboards: TTabSheet; Panel5: TPanel; Label2: TLabel; @@ -174,7 +169,6 @@ type lblCompileTargetHeader: TLabel; cmdInstall: TButton; cmdUninstall: TButton; - Label5: TLabel; panBuildWindowsInstaller: TPanel; Label9: TLabel; lblBootstrapMSI: TLabel; @@ -198,6 +192,18 @@ type chkLexicalModelRTL: TCheckBox; editLexicalModelFilename: TEdit; imgQRCode: TImage; + panOpenInExplorer: TPanel; + lblOpenInExplorer: TLabel; + cmdOpenSourceFolder: TButton; + cmdOpenBuildFolder: TButton; + cmdOpenProjectFolder: TButton; + panFileActions: TPanel; + lblFileActions: TLabel; + editOutPath: TEdit; + cmdCompileInstaller: TButton; + cmdAddToProject: TButton; + cmdBuildPackage: TButton; + Label5: TLabel; procedure cmdCloseClick(Sender: TObject); procedure cmdAddFileClick(Sender: TObject); procedure cmdRemoveFileClick(Sender: TObject); @@ -226,7 +232,6 @@ type procedure editStartMenuPathChange(Sender: TObject); procedure cbKMPImageFileClick(Sender: TObject); procedure cmdUninstallClick(Sender: TObject); - procedure cmdOpenContainingFolder2Click(Sender: TObject); procedure cmdOpenContainingFolderClick(Sender: TObject); procedure cmdOpenFileClick(Sender: TObject); procedure cmdCompileInstallerClick(Sender: TObject); @@ -256,6 +261,9 @@ type procedure chkLexicalModelRTLClick(Sender: TObject); procedure editLexicalModelDescriptionChange(Sender: TObject); procedure lbDebugHostsClick(Sender: TObject); + procedure cmdOpenSourceFolderClick(Sender: TObject); + procedure cmdOpenBuildFolderClick(Sender: TObject); + procedure cmdOpenProjectFolderClick(Sender: TObject); private pack: TKPSFile; FSetup: Integer; @@ -1043,16 +1051,6 @@ begin lbStartMenuEntriesClick(lbStartMenuEntries); end; -procedure TfrmPackageEditor.cmdOpenContainingFolder2Click(Sender: TObject); -begin - OpenContainingFolder(FileName); -end; - -procedure TfrmPackageEditor.cmdOpenContainingFolderClick(Sender: TObject); -begin - OpenContainingFolder((lbFiles.Items.Objects[lbFiles.ItemIndex] as TPackageContentFile).FileName); -end; - procedure TfrmPackageEditor.cmdOpenDebugHostClick(Sender: TObject); begin TUtilExecute.URL(lbDebugHosts.Items[lbDebugHosts.ItemIndex] + '/packages.html'); @@ -1083,6 +1081,27 @@ begin end; end; +procedure TfrmPackageEditor.cmdOpenSourceFolderClick(Sender: TObject); +begin + OpenContainingFolder(FileName); +end; + +procedure TfrmPackageEditor.cmdOpenBuildFolderClick(Sender: TObject); +begin + OpenContainingFolder((ProjectFile as TkpsProjectFile).TargetFilename); +end; + +procedure TfrmPackageEditor.cmdOpenProjectFolderClick(Sender: TObject); +begin + if Assigned(ProjectFile.Project) then + OpenContainingFolder(ProjectFile.Project.FileName); +end; + +procedure TfrmPackageEditor.cmdOpenContainingFolderClick(Sender: TObject); +begin + OpenContainingFolder((lbFiles.Items.Objects[lbFiles.ItemIndex] as TPackageContentFile).FileName); +end; + procedure TfrmPackageEditor.cmdDeleteStartMenuEntryClick(Sender: TObject); var n: Integer; @@ -1195,17 +1214,15 @@ begin for i := 0 to pack.Info.Count - 1 do UpdateInfo(pack.Info[i].Name, pack.Info[i].Description, pack.Info[i].URL); chkFollowKeyboardVersion.Checked := pack.KPSOptions.FollowKeyboardVersion; - EnableDetailsTabControls; lbStartMenuEntries.Clear; - + for i := 0 to pack.StartMenu.Entries.Count - 1 do lbStartMenuEntries.Items.AddObject(pack.StartMenu.Entries[i].Name, pack.StartMenu.Entries[i]); chkCreateStartMenu.Checked := pack.StartMenu.DoCreate; chkStartMenuUninstall.Checked := pack.StartMenu.AddUninstallEntry; editStartMenuPath.Text := pack.StartMenu.Path; - EnableStartMenuControls; editOutPath.Text := (ProjectFile as TkpsProjectFile).TargetFilename; // I4688 editBootstrapMSI.Text := pack.KPSOptions.MSIFileName; @@ -1220,6 +1237,8 @@ begin RefreshKeyboardList; RefreshLexicalModelList; + + EnableControls; finally Dec(FSetup); end; @@ -1547,6 +1566,12 @@ end; procedure TfrmPackageEditor.EnableCompileTabControls; begin cmdOpenDebugHost.Enabled := lbDebugHosts.ItemIndex >= 0; + + // We use FProjectFile because we don't want to accidentally create a standalone + // project file as GetProjectFile is side-effecty. EnableControls is called early + // in construction before FProjectFile is assigned. It is called again later so + // enabled state will be correct. + cmdOpenProjectFolder.Enabled := Assigned(FProjectFile) and Assigned(FProjectFile.Project); end; procedure TfrmPackageEditor.EnableDetailsTabControls; diff --git a/windows/src/developer/TIKE/project/Keyman.Developer.UI.Project.UfrmProject.pas b/windows/src/developer/TIKE/project/Keyman.Developer.UI.Project.UfrmProject.pas index 02aea2a260..b65fbcfe09 100644 --- a/windows/src/developer/TIKE/project/Keyman.Developer.UI.Project.UfrmProject.pas +++ b/windows/src/developer/TIKE/project/Keyman.Developer.UI.Project.UfrmProject.pas @@ -460,6 +460,20 @@ begin else if SelectedMRUFileName <> '' then OpenContainingFolder(SelectedMRUFileName); end + else if Command = 'openbuildfolder' then + begin + pf := SelectedProjectFile; + if Assigned(pf) and (pf is TkmnProjectFile) then + OpenContainingFolder((pf as TkmnProjectFile).TargetFileName) + else if Assigned(pf) and (pf is TkpsProjectFile) then + OpenContainingFolder((pf as TkpsProjectFile).TargetFileName) + else if Assigned(pf) and (pf is TmodelTsProjectFile) then + OpenContainingFolder((pf as TmodelTsProjectFile).TargetFileName); + end + else if Command = 'openprojectfolder' then + begin + OpenContainingFolder(FGlobalProject.FileName); + end else if Command = 'removefile' then begin pf := SelectedProjectFile; diff --git a/windows/src/developer/TIKE/xml/project/keyboards.xsl b/windows/src/developer/TIKE/xml/project/keyboards.xsl index 894a018fa7..9da09d7df3 100644 --- a/windows/src/developer/TIKE/xml/project/keyboards.xsl +++ b/windows/src/developer/TIKE/xml/project/keyboards.xsl @@ -163,10 +163,15 @@ - Open Containing Folder + Open Source Folder keyman:opencontainingfolder?id= + + Open Build Folder + keyman:openbuildfolder?id= + + Remove from Project keyman:removefile?id= diff --git a/windows/src/developer/TIKE/xml/project/models.xsl b/windows/src/developer/TIKE/xml/project/models.xsl index cc45fa3650..a1a41286b9 100644 --- a/windows/src/developer/TIKE/xml/project/models.xsl +++ b/windows/src/developer/TIKE/xml/project/models.xsl @@ -149,10 +149,15 @@ - Open Containing Folder + Open Source Folder keyman:opencontainingfolder?id= + + Open Build Folder + keyman:openbuildfolder?id= + + Remove from Project keyman:removefile?id= diff --git a/windows/src/developer/TIKE/xml/project/packages.xsl b/windows/src/developer/TIKE/xml/project/packages.xsl index 96361f02d7..94d45113cc 100644 --- a/windows/src/developer/TIKE/xml/project/packages.xsl +++ b/windows/src/developer/TIKE/xml/project/packages.xsl @@ -140,9 +140,14 @@ keyman:editfileexternal?id= - Open Containing Folder + Open Source Folder keyman:opencontainingfolder?id= + + Open Build Folder + keyman:openbuildfolder?id= + + Remove from Project keyman:removefile?id= diff --git a/windows/src/developer/TIKE/xml/project/welcome.xsl b/windows/src/developer/TIKE/xml/project/welcome.xsl index 35cc14f814..cc38756165 100644 --- a/windows/src/developer/TIKE/xml/project/welcome.xsl +++ b/windows/src/developer/TIKE/xml/project/welcome.xsl @@ -47,6 +47,11 @@
+ + Open project folder + keyman:openprojectfolder + +

Recent Files

diff --git a/windows/src/global/delphi/general/utilsystem.pas b/windows/src/global/delphi/general/utilsystem.pas index 1e014f722e..2117b2fa81 100644 --- a/windows/src/global/delphi/general/utilsystem.pas +++ b/windows/src/global/delphi/general/utilsystem.pas @@ -329,11 +329,13 @@ var sei: TShellExecuteInfo; s: string; begin - s := '/select,'+FileName; + if not FileExists(FileName) + then s := '"'+ExtractFilePath(FileName)+'"' + else s := '"/select,'+FileName+'"'; FillChar(sei, SizeOf(sei), 0); sei.cbSize := SizeOf(TShellExecuteInfo); sei.lpVerb := 'open'; - sei.lpFile := 'explorer'; //PChar(s); + sei.lpFile := 'explorer'; sei.lpParameters := PChar(s); sei.nShow := SW_SHOWNORMAL; ShellExecuteEx(@sei); From 77c333da52f300936b06498849024d200af4e00d Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Wed, 3 Mar 2021 11:41:45 +0700 Subject: [PATCH 082/125] fix(android/engine): Cleanup KMHelpFileActivity --- .../main/java/com/tavultesoft/kmea/KMHelpFileActivity.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java index 5e980bc56d..fc0e5ddd17 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHelpFileActivity.java @@ -53,9 +53,6 @@ public class KMHelpFileActivity extends BaseActivity { super.onCreate(savedInstanceState); final Context context = this; - // Teardown keyboards to free up webView - KMManager.onDestroy(); - // Different processes in the same application cannot directly share WebView-related data // https://developer.android.com/reference/android/webkit/WebView.html#setDataDirectorySuffix(java.lang.String) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { @@ -170,6 +167,7 @@ public class KMHelpFileActivity extends BaseActivity { @Override protected void onDestroy() { super.onDestroy(); + WebViewUtil.cleanup(webView); } @Override From 0f7f2739f580d587ebd0c96248115bbf75168d39 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 3 Mar 2021 13:01:49 +0700 Subject: [PATCH 083/125] fix(ios): better ux for net-delayed package installations, kbd display mgmt --- .../AssociatingPackageInstaller.swift | 26 +++++-- .../PackageInstallViewController.swift | 76 ++++++++++++++++--- .../ResourceFileManager.swift | 16 ++-- .../Keyman/PackageBrowserViewController.swift | 4 +- 4 files changed, 90 insertions(+), 32 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/AssociatingPackageInstaller.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/AssociatingPackageInstaller.swift index 9eb8791b9a..2ac9436d30 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/AssociatingPackageInstaller.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/AssociatingPackageInstaller.swift @@ -121,7 +121,9 @@ public class AssociatingPackageInstaller Void)) { + public func promptForLanguages(inNavigationVC navVC: UINavigationController) { guard self.associationQueriers == nil, !closureShared.pickingCompleted else { fatalError("Invalid state - language picking has already been triggered.") } @@ -400,7 +408,6 @@ public class AssociatingPackageInstaller: UIViewCon private var navMapping: [NavigationMode : UIBarButtonItem] = [:] private var dismissalBlock: (() -> Void)? = nil + private weak var welcomeView: UIView? public init(for package: Resource.Package, defaultLanguageCode: String? = nil, @@ -349,20 +350,27 @@ public class PackageInstallViewController: UIViewCon Manager.shared.shouldReloadKeyboard = true self.pickingCompletionHandler(selectedResources.map { $0.typedFullID }) - let dismissalBlock = { - if let nvc = self.navigationController { - self.dismiss(animated: true) - nvc.popToRootViewController(animated: false) - } else { // Otherwise, if the root view of a navigation controller, dismiss it outright. (pop not available) - self.dismiss(animated: true) - } + // Prevent swipe dismissal. + if #available(iOSApplicationExtension 13.0, *) { + self.isModalInPresentation = true + } + // No more selection-manipulation allowed. + // This matters when there's no welcome page available. + languageTable.isUserInteractionEnabled = false + // Prevent extra 'install' commands. + self.rightNavigationMode = .none + + let dismissalBlock = { self.associators.forEach { $0.pickerFinalized() } } // First, show the package's welcome - if it exists. if let welcomeVC = PackageWebViewController(for: package, page: .welcome) { - self.dismissalBlock = dismissalBlock + // Prevent swipe dismissal. + if #available(iOSApplicationExtension 13.0, *) { + welcomeVC.isModalInPresentation = true + } let subNavVC = UINavigationController(rootViewController: welcomeVC) _ = subNavVC.view @@ -383,6 +391,13 @@ public class PackageInstallViewController: UIViewCon subNavVC.presentationController?.delegate = self self.present(subNavVC, animated: true, completion: nil) + self.welcomeView = welcomeVC.view + + self.dismissalBlock = { + // Tells the user that we've received the 'done' command. + doneItem.isEnabled = false + dismissalBlock() + } } else { dismissalBlock() } @@ -393,10 +408,33 @@ public class PackageInstallViewController: UIViewCon } @objc private func onWelcomeDismissed() { - self.dismissalBlock?() - self.dismissalBlock = nil + if let dismissalBlock = self.dismissalBlock { + dismissalBlock() + self.dismissalBlock = nil - self.uiCompletionHandler() + // Tell our owner (the AssociatingPackageInstaller) that all UI interactions are done. + // Triggers synchronization code, so make sure it only runs once! + self.uiCompletionHandler() + } + + // Show a spinner forever. + // When installation is complete, this controller's view will be dismissed, + // removing said spinner. + let activitySpinner = Alerts.constructActivitySpinner() + + // Determine the top-most view. If we're presenting the welcome page, THAT. + // If we aren't, our directly-owned view should be the top-most. + let activeView: UIView = self.welcomeView ?? view + + activitySpinner.center = activeView.center + activitySpinner.startAnimating() + activeView.addSubview(activitySpinner) + + activitySpinner.centerXAnchor.constraint(equalTo: activeView.centerXAnchor).isActive = true + activitySpinner.centerYAnchor.constraint(equalTo: activeView.centerYAnchor).isActive = true + + // Note: we do NOT block user interaction; we merely bind them to the active view. + // Why prevent them from reading more on the welcome page while they wait? } public func tableView(_ tableView: UITableView, titleForHeaderInSection: Int) -> String? { @@ -515,4 +553,20 @@ public class PackageInstallViewController: UIViewCon associators.forEach { $0.deselectLanguages( Set([languages[indexPath.row].id]) ) } } + + internal func progressUpdate>(_ status: AssociatingPackageInstaller.Progress) where Resource.Package == Package { + switch(status) { + case .starting, .inProgress: + // nothing worth note + break + // All UI interactions have been completed AND installation is fully complete. + case .complete, .cancelled: + if let nvc = self.navigationController { + self.dismiss(animated: true) + nvc.popToRootViewController(animated: false) + } else { // Otherwise, if the root view of a navigation controller, dismiss it outright. (pop not available) + self.dismiss(animated: true) + } + } + } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift index 2039f78480..68ab97b707 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift @@ -228,27 +228,23 @@ public class ResourceFileManager { activitySpinner.centerXAnchor.constraint(equalTo: rootVC.view.centerXAnchor).isActive = true activitySpinner.centerYAnchor.constraint(equalTo: rootVC.view.centerYAnchor).isActive = true rootVC.view.isUserInteractionEnabled = false - } else if status == .complete { + } else if status == .complete || status == .cancelled { // Report completion! activitySpinner.stopAnimating() activitySpinner.removeFromSuperview() rootVC.view.isUserInteractionEnabled = true - rootVC.dismiss(animated: true, completion: nil) + rootVC.dismiss(animated: true) { + Manager.shared.showKeyboard() + } successHandler?(package) } } if let navVC = rootVC as? UINavigationController { - packageInstaller.promptForLanguages(inNavigationVC: navVC) { - // The user will be on the main screen after this, so we should resummon the keyboard. - Manager.shared.showKeyboard() - } + packageInstaller.promptForLanguages(inNavigationVC: navVC) } else { let nvc = UINavigationController.init() - packageInstaller.promptForLanguages(inNavigationVC: nvc) { - // The user will be on the main screen after this, so we should resummon the keyboard. - Manager.shared.showKeyboard() - } + packageInstaller.promptForLanguages(inNavigationVC: nvc) rootVC.present(nvc, animated: true, completion: nil) } } diff --git a/ios/keyman/Keyman/Keyman/PackageBrowserViewController.swift b/ios/keyman/Keyman/Keyman/PackageBrowserViewController.swift index ad27110129..7388147997 100644 --- a/ios/keyman/Keyman/Keyman/PackageBrowserViewController.swift +++ b/ios/keyman/Keyman/Keyman/PackageBrowserViewController.swift @@ -116,9 +116,7 @@ class PackageBrowserViewController: UIDocumentPickerViewController, UIDocumentPi } if let navVC = self.navVC { - packageInstaller.promptForLanguages(inNavigationVC: navVC) { - // do nothing; the Settings menu dismissal will take care of displaying the keyboard - } + packageInstaller.promptForLanguages(inNavigationVC: navVC) } } } From d2a70127169ec880c51b7d0fcdb4aa7f00a4845e Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 3 Mar 2021 13:21:49 +0700 Subject: [PATCH 084/125] fix(ios): handling of cases with no welcome.htm --- .../PackageInstallViewController.swift | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/PackageInstallViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/PackageInstallViewController.swift index ac2d20d316..748edfacab 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/PackageInstallViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/PackageInstallViewController.swift @@ -60,6 +60,7 @@ public class PackageInstallViewController: UIViewCon private var dismissalBlock: (() -> Void)? = nil private weak var welcomeView: UIView? + private var mayPick: Bool = true public init(for package: Resource.Package, defaultLanguageCode: String? = nil, @@ -268,9 +269,11 @@ public class PackageInstallViewController: UIViewCon } set(mode) { - leftNavMode = mode + if mayPick { + leftNavMode = mode - navigationItem.leftBarButtonItem = navMapping[mode] + navigationItem.leftBarButtonItem = navMapping[mode] + } } } @@ -280,9 +283,11 @@ public class PackageInstallViewController: UIViewCon } set(mode) { - rightNavMode = mode + if mayPick { + rightNavMode = mode - navigationItem.rightBarButtonItem = navMapping[mode] + navigationItem.rightBarButtonItem = navMapping[mode] + } } } @@ -358,8 +363,10 @@ public class PackageInstallViewController: UIViewCon // No more selection-manipulation allowed. // This matters when there's no welcome page available. languageTable.isUserInteractionEnabled = false - // Prevent extra 'install' commands. + // Prevent extra 'install' commands and nav-bar related manipulation. + self.navigationItem.leftBarButtonItem?.isEnabled = false self.rightNavigationMode = .none + self.mayPick = false let dismissalBlock = { self.associators.forEach { $0.pickerFinalized() } @@ -399,7 +406,8 @@ public class PackageInstallViewController: UIViewCon dismissalBlock() } } else { - dismissalBlock() + self.dismissalBlock = dismissalBlock + onWelcomeDismissed() } } From a78d1d76cf49ffdec24b7258a42042a305b6df09 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 3 Mar 2021 14:24:37 +0700 Subject: [PATCH 085/125] fix(ios): adds i18n for some error alerts --- .../ResourceFileManager.swift | 5 +++-- .../LanguageSettingsViewController.swift | 2 +- .../KeymanEngine/en.lproj/Localizable.strings | Bin 19628 -> 19782 bytes .../KeymanEngineDemo/MainViewController.swift | 5 +++-- .../WebBrowserViewController.swift | 10 +++++++--- .../Keyman/en.lproj/Localizable.strings | Bin 5860 -> 6076 bytes 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift index 2039f78480..375279e491 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift @@ -265,13 +265,14 @@ public class ResourceFileManager { } public func buildKMPError(_ error: KMPError) -> UIAlertController { - return buildSimpleAlert(title: "Error", message: error.localizedDescription) + return buildSimpleAlert(title: NSLocalizedString("alert-error-title", bundle: engineBundle, comment: ""), + message: error.localizedDescription) } public func buildSimpleAlert(title: String, message: String, completionHandler: (() -> Void)? = nil ) -> UIAlertController { let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert) - alertController.addAction(UIAlertAction(title: "OK", + alertController.addAction(UIAlertAction(title: NSLocalizedString("command-ok", bundle: engineBundle, comment: ""), style: UIAlertAction.Style.default, handler: { _ in completionHandler?() diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index 90aac970f0..9d4a270120 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -253,7 +253,7 @@ class LanguageSettingsViewController: UITableViewController { } default: - cell.textLabel?.text = "error" + cell.textLabel?.text = NSLocalizedString("alert-error-title", bundle: engineBundle, comment: "") } } } diff --git a/ios/engine/KMEI/KeymanEngine/en.lproj/Localizable.strings b/ios/engine/KMEI/KeymanEngine/en.lproj/Localizable.strings index b13b4ec2dca8c6f8b4c841e1081f7ac0d8e6404a..8214a9a745c011a17ef6b632dcea8a52ca668720 100644 GIT binary patch delta 82 zcmZ28lkwOr#tlE1COa@m7^gF&GUNeq5kn$F4ub-i1o2A1>?DSKhEgyaBvu6E6)_Y~ VZe$UkypBm>a+WIB=2uLIJOPHO73cr} delta 18 acmX>$i*e0N#tlE1CQlG{*nEk(#1jBfbO;Rq diff --git a/ios/engine/KMEI/KeymanEngineDemo/MainViewController.swift b/ios/engine/KMEI/KeymanEngineDemo/MainViewController.swift index e72ffd6294..1b84fb6259 100644 --- a/ios/engine/KMEI/KeymanEngineDemo/MainViewController.swift +++ b/ios/engine/KMEI/KeymanEngineDemo/MainViewController.swift @@ -227,10 +227,11 @@ class MainViewController: UIViewController, UIAlertViewDelegate, TextViewDelegat } @objc func showAlert(_ message: String) { - let alertController = UIAlertController(title: "Keyboard Download Error", + let engineBundle = Bundle(for: Manager.self) + let alertController = UIAlertController(title: NSLocalizedString("alert-download-error-title", bundle: engineBundle, comment: ""), message: message, preferredStyle: UIAlertControllerStyle.alert) - alertController.addAction(UIAlertAction(title: "OK", + alertController.addAction(UIAlertAction(title: NSLocalizedString("command-ok", bundle: engineBundle, comment: ""), style: UIAlertActionStyle.default, handler: nil)) self.present(alertController, animated: true, completion: nil) diff --git a/ios/keyman/Keyman/Keyman/KMWebBrowser/WebBrowserViewController.swift b/ios/keyman/Keyman/Keyman/KMWebBrowser/WebBrowserViewController.swift index 8e6e7f9b53..c6f1af76d4 100644 --- a/ios/keyman/Keyman/Keyman/KMWebBrowser/WebBrowserViewController.swift +++ b/ios/keyman/Keyman/Keyman/KMWebBrowser/WebBrowserViewController.swift @@ -218,7 +218,9 @@ class WebBrowserViewController: UIViewController, UIWebViewDelegate, UIAlertView ResourceDownloadManager.shared.downloadRawKMP(from: request.url!) { file, error in // do something! if let error = error { - let alert = ResourceFileManager.shared.buildSimpleAlert(title: "Error", message: error.localizedDescription) + let alertTitle = NSLocalizedString("alert-error-title", bundle: Bundle(for: Manager.self), comment: "") + let alert = ResourceFileManager.shared.buildSimpleAlert(title: alertTitle, + message: error.localizedDescription) self.present(alert, animated: true, completion: nil) return } @@ -263,10 +265,12 @@ class WebBrowserViewController: UIViewController, UIWebViewDelegate, UIAlertView } if signalError { - let alertController = UIAlertController(title: "Cannot Open Page", + let alertController = UIAlertController(title: NSLocalizedString("error-opening-page", comment: ""), message: error.localizedDescription, preferredStyle: UIAlertController.Style.alert) - alertController.addAction(UIAlertAction(title: "OK", + alertController.addAction(UIAlertAction(title: NSLocalizedString("command-ok", + bundle: Bundle(for: Manager.self), + comment: ""), style: UIAlertAction.Style.default, handler: nil)) self.present(alertController, animated: true, completion: nil) diff --git a/ios/keyman/Keyman/Keyman/en.lproj/Localizable.strings b/ios/keyman/Keyman/Keyman/en.lproj/Localizable.strings index 9395b890facdc1f40ea4f0cfa0b62796c141774e..354b5303c565bee2a0427a18167321862d68e18e 100644 GIT binary patch delta 215 zcmaE&yGMUR980|?Lmop4LncEqLn1>7Ln=ctg94D12P9J&ihwX5h!ucj0g#^uRF}t) z4pyPSP!1GH0eGR21)$1QhC+r?AXyAHD+MT)#83%UUkVfjnW?~_#h}l?#h?V@ sc|onvMYBQ|ZmbeegDns%F*t)AmIqW>0_6Kcout4J0CB)(f0n620QS=;bN~PV delta 12 Tcmdm^|3r5~9Lr`i);1vkBMbzU From f1133d0fc98adf4d59118f9195e31dc4a8e500de Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Wed, 3 Mar 2021 14:45:53 +0700 Subject: [PATCH 086/125] chore(android): Check in crowdin files for de --- .../KMAPro/kMAPro/src/main/res/values-de-rDE/strings.xml | 6 +++++- android/KMEA/app/src/main/res/values-de-rDE/strings.xml | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/res/values-de-rDE/strings.xml b/android/KMAPro/kMAPro/src/main/res/values-de-rDE/strings.xml index 6fae23d0b9..8eb6ddda70 100644 --- a/android/KMAPro/kMAPro/src/main/res/values-de-rDE/strings.xml +++ b/android/KMAPro/kMAPro/src/main/res/values-de-rDE/strings.xml @@ -43,7 +43,7 @@ Zeige \"%1$s\" beim Start - Um Tastaturpakete zu installieren, braucht Keyman die Berechtigung zum Lesen/Schreiben. + Um Tastaturpakete zu installieren, braucht Keyman die Berechtigung zum Lesen von externem Speicher. Zugriff auf den Speicher wurde abgelehnt. Möglicherweise funktioniert die Installation des Tastaturpakets nicht korrekt @@ -56,6 +56,8 @@ Tastatur oder Wörterbuch installieren + Anzeigesprache ändern + Banner immer anzeigen Nicht implementiert @@ -108,6 +110,8 @@ Wörterbuch installieren + %1$s ist keine gültige Keyman Paketdatei.\n%2$s\" + Tastaturpaket enthält keine berührungsoptimierte Tastatur, die installiert werden könnte Kein neues Wörterbuch zu installieren diff --git a/android/KMEA/app/src/main/res/values-de-rDE/strings.xml b/android/KMEA/app/src/main/res/values-de-rDE/strings.xml index 452d732bf2..04cfa8730e 100644 --- a/android/KMEA/app/src/main/res/values-de-rDE/strings.xml +++ b/android/KMEA/app/src/main/res/values-de-rDE/strings.xml @@ -63,7 +63,7 @@ [neu] %1$s Scannen Sie diesen Code, um diese\nTastatur auf einem anderen Gerät zu laden - + Willkommen bei %1$s Suche nach zugehörigem Wörterbuch zum Herunterladen @@ -91,6 +91,10 @@ Das ausgewählte Wörterbuch wird bereits heruntergeladen. Bitte versuchen Sie es gleich nochmal! Download des Wörterbuchs ist abgeschlossen. + + Fehler beim Zugriff auf die heruntergeladene Datei + + Laden der heruntergeladenen Datei fehlgeschlagen Fehler beim Zugriff auf den Server! @@ -123,6 +127,8 @@ (%1$d Tastatur) (%1$d Tastaturen) + + Standardsprache From 2c6bff4f4372a33ee6117323282c93f418acb87a Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Wed, 3 Mar 2021 14:47:16 +0700 Subject: [PATCH 087/125] chore(linux): Check in crowdin files for de --- linux/keyman-config/locale/de_DE.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/keyman-config/locale/de_DE.po b/linux/keyman-config/locale/de_DE.po index 9763ce6ef3..5824946c84 100644 --- a/linux/keyman-config/locale/de_DE.po +++ b/linux/keyman-config/locale/de_DE.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: keyman\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-08-19 19:17+0200\n" -"PO-Revision-Date: 2021-01-19 08:16\n" +"PO-Revision-Date: 2021-03-03 07:44\n" "Last-Translator: \n" "Language-Team: German\n" "Language: de_DE\n" From f442dee6156e2e21ab0913985fe006b1b0534ea2 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Wed, 3 Mar 2021 14:47:43 +0700 Subject: [PATCH 088/125] chore(windows): Check in crowdin files for de --- .../src/desktop/kmshell/locale/de/strings.xml | 123 +++++++++--------- .../src/desktop/setup/locale/de/strings.xml | 8 +- 2 files changed, 65 insertions(+), 66 deletions(-) diff --git a/windows/src/desktop/kmshell/locale/de/strings.xml b/windows/src/desktop/kmshell/locale/de/strings.xml index f9cd9b8d31..152f8f1417 100644 --- a/windows/src/desktop/kmshell/locale/de/strings.xml +++ b/windows/src/desktop/kmshell/locale/de/strings.xml @@ -52,6 +52,10 @@ Keyman läuft noch. Klicken Sie auf dieses Symbol, um die Tastatur für Ihre Sprache jederzeit zu verwenden + + + + Keyman läuft bereits. Klicken Sie auf das Keyman-Symbol im Benachrichtigungsbereich um Ihre Sprachtastatur zu verwenden @@ -224,11 +228,11 @@ Es sind keine Tastaturen installiert. Klicken Sie auf \'Tastatur herunterladen\', um eine Tastatur von der Keyman-Website zu installieren. - + - Sie können die \'%0:s\'-Tastatur nur deinstallieren, wenn Sie ein Administrator sind + Sie können die \'%1$s\' -Tastatur nur deinstallieren, wenn Sie ein Administrator sind @@ -340,7 +344,7 @@ - Benutzer Berechtigungen werden immer in Windows Vista erhöht + Benutzer Berechtigungen werden immer in Windows Vista erhöht @@ -365,7 +369,7 @@ - Keyman Menü öffnen + Keyman Menü öffnen @@ -450,11 +454,11 @@ Nicht installieren, nur herunterladen - + - Kann Tastatur nicht herunterladen. Fehler %1:d: %0:s + Kann Tastatur nicht herunterladen. Fehler %2$d: %1$s @@ -507,30 +511,30 @@ Tastenkürzel ändern - + - Standard-Tastenkürzel selektieren, oder \'Benutzerdefiniert\' auswählen und Strg, Umschalt- und/oder Alt- sowie gewünschte Taste für die Sprache %0:s drücken: + Standard-Tastenkürzel selektieren, oder \'Benutzerdefiniert\' auswählen und Strg, Umschalt- und/oder Alt- sowie Taste für das gewünschte Tastaturkürzel für die Sprache %1$s drücken: Standard-Tastenkürzel selektieren, oder \'Benutzerdefiniert\' auswählen und Strg, Umschalt- und/oder Alt- sowie Taste für das gewünschte Tastaturkürzel drücken: - + - Das Tastenkürzel %0:s steht im Konflikt mit der normalen Tastatur. Sie sollten mindestens Strg oder Alt verwenden. Möchten Sie dies jetzt ändern? - + Das Tastenkürzel %1$s steht im Konflikt mit der normalen Tastatur. Sie sollten mindestens Strg oder Alt verwenden. Möchten Sie dies jetzt ändern\? + - Das Tastenkürzel %0:s steht im Konflikt zu dem ausgewählten Tastenkürzel für die %1:s-Tastatur. Wenn Sie fortfahren wird das Tastenkürzel für %1:s gelöscht. Fortfahren? - + Das Tastenkürzel %1$s steht im Konflikt zu dem ausgewählten Tastenkürzel für die %2$s-Tastatur. Wenn Sie fortfahren wird das Tastenkürzel für %2$s gelöscht. Fortfahren\? + - Das Tastenkürzel %0:s steht in Konflikt mit einem anderen Tastenkürzel. Wenn Sie fortfahren, wird das andere Tastenkürzel gelöscht. Fortfahren? + Das Tastenkürzel %1$s steht in Konflikt mit einem anderen Tastenkürzel. Wenn Sie fortfahren, wird das andere Tastenkürzel gelöscht. Fortfahren? @@ -567,25 +571,25 @@ Größe - + - Keyman %0:s - + Keyman %1$s + - %0:s-Tastatur %1:s + Keyboard %1$s %2$s Die keyman.com-Webseite ist nicht erreichbar - bitte testen Sie Ihre Internet-Verbindung, und versuchen Sie es erneut. - + - Die keyman.com-Webseite ist nicht erreichbar - bitte testen Sie Ihre Internet-Verbindung, und versuchen Sie es erneut. Der Fehler war: %0:s + Die keyman.com-Webseite ist nicht erreichbar - bitte testen Sie Ihre Internet-Verbindung, und versuchen Sie es erneut. Der Fehler war: %1$s @@ -666,12 +670,12 @@ - Keyman verlassen? + Keyman beenden\? - Sind Sie sicher, dass Sie Keyman verlassen möchten? Ihre Keyman-Tastaturen werden weiterhin in den Windows-Sprachen aufgelistet, werden aber erst dann funktionieren, wenn Sie Keyman neu starten. + Sind Sie sicher, dass Sie Keyman verlassen möchten\? Ihre Keyman-Tastaturen werden weiterhin in den Windows-Sprachen aufgelistet, werden aber erst dann funktionieren, wenn Sie Keyman neu starten. @@ -778,35 +782,40 @@ Keyman - + - Version %0:s + Version %1$s &Drucken... - + - Ein Paket mit dem Namen %0:s ist bereits installiert. Möchten Sie es deinstallieren und das neue installieren? - + Ein Paket mit dem Namen %1$s ist bereits installiert. Möchten Sie es deinstallieren und das neue installieren\? + - Eine Tastatur mit dem Name \'%0:s\' ist bereits installiert. Wenn Sie fortfahren, wird sie deinstalliert bevor die neue Tastatur installiert wird. Fortfahren? - + Eine Tastatur mit dem Namen \'%1$s\' ist bereits installiert. Wenn Sie fortfahren, wird sie deinstalliert, bevor die neue Tastatur installiert wird. Fortfahren\? + - Die Tastatur \'%0:s\' ist Teil des Pakets \'%1:s\'. Sie müssen das gesamte Paket deinstallieren. Fortfahren? - + Die Tastatur \'%1$s\' ist Teil des Pakets \'%2$s\'. Sie müssen das gesamte Paket deinstallieren. Fortfahren\? + + + + + Die Tastatursprache kann nicht installiert werden; Windows hat ein Limit von 4 benutzerdefinierten \'transienten\' Sprachen, und Sie haben möglicherweise dieses Limit erreicht. + - Das Paket oder die Tastatur \'%0:s\' enthält keine Einführungshilfe. + Das Paket oder die Tastatur \'%1$s\' enthält keine Einführungshilfe. @@ -831,72 +840,62 @@ Datei wird heruntergeladen - + - Sind Sie sicher, dass Sie die Bildschirmtastatur für %0:s entfernen möchten? - + Sind Sie sicher, dass Sie die Bildschirmtastatur für %1$s entfernen möchten\? + - Sind Sie sicher, dass Sie die Tastatur %0:s deinstallieren wollen? - + Sind Sie sicher, dass Sie die Tastatur %1$s deinstallieren wollen\? + - Sind Sie sicher, dass Sie das Tastaturbelegungspaket %0:s deinstallieren wollen? - -%1:s - + Sind Sie sicher, dass Sie das Paket %1$s deinstallieren möchten\?\n\n%2$s + - Die folgenden Schriftarten werden ebenfalls deinstalliert: %0:s. + Die folgenden Schriftarten werden ebenfalls deinstalliert: %1$s. - Die Zeichentabelle enthält eine Datenbank von Zeichen, die erstellt werden muss, bevor sie verwendet werden kann. Jetzt erstellen? - + Die Zeichentabelle enthält eine Datenbank von Zeichen, die erstellt werden muss, bevor sie verwendet werden kann. Jetzt erstellen\? + - Die Unicode Zeichen-Datenbank konnte nicht gelöscht werden für einen Wiederaufbau. Der Fehler ist: %0:s - + Die Unicode Zeichen-Datenbank konnte nicht gelöscht werden für einen Wiederaufbau. Der Fehler ist: %1$s + - Die Unicode Zeichen-Datenbank konnte nicht erstellt werden und wurde deaktiviert. Der Fehler ist: %0:s - + Die Unicode-Zeichen-Datenbank konnte nicht erstellt werden und wurde deaktiviert. Fehlerdetails: %1$s + - Die Unicode Zeichen-Datenbank konnte nicht geladen werden (%0:s). Jetzt neu erstellen? - + Die Unicode Zeichen-Datenbank konnte nicht geladen werden (%1$s). Jetzt neu erstellen\? + - Keyman konnte nicht gestartet werden. Bevor Sie fortfahren überprüfen Sie bitte Ihre Sicherheitseinstellungen, um sicherzustellen, dass das Programm keyman.exe gestartet werden darf. Der gemeldete Fehler ist: - -\"%0:s\" - -Möchten Sie Keyman erneut starten? + Keyman konnte nicht gestartet werden. Bevor Sie fortfahren überprüfen Sie bitte Ihre Sicherheitseinstellungen, um sicherzustellen, dass das Programm keyman.exe gestartet werden darf. Der gemeldete Fehler ist:\n\n\"%1$s\"\n\nMöchten Sie Keyman erneut starten\? - Keyman Debug-Informationen werden in einer Protokolldatei mit dem Namen %LOCALAPPDATA%\Keyman\Diag\system#.etl gespeichert (wobei # eine Nummer ist). Sie sollten Keyman beenden, bevor Sie diese Datei löschen. - -WARNUNG: Diese Datei kann sehr schnell groß werden. Das Aktivieren der Debug-Protokollierung kann Ihr System verlangsamen und sollte nur auf Anweisung durch den technischen Support erfolgen. - -DATENSCHUTZ-HINWEIS: Bitte beachten Sie, dass die Debug-Logdatei alle von Ihnen eingegebenen Tastenanschläge aufzeichnet. Sie sollten das Debug-Log nur für die Dauer einer Debugging- oder Diagnosesitzung aktivieren. + Keyman Debug-Informationen werden in einer Protokolldatei mit dem Namen %LOCALAPPDATA%\Keyman\Diag\system#.etl gespeichert (wobei # eine Nummer ist). Sie sollten Keyman beenden, bevor Sie diese Datei löschen.\n\nWARNUNG: Diese Datei kann sehr schnell groß werden. Das Aktivieren der Debug-Protokollierung kann Ihr System verlangsamen und sollte nur auf Anweisung durch den technischen Support erfolgen.\n\nDATENSCHUTZ-HINWEIS: Bitte beachten Sie, dass die Debug-Logdatei alle von Ihnen eingegebenen Tastenanschläge aufzeichnet. Sie sollten das Debug-Log nur für die Dauer einer Debugging- oder Diagnosesitzung aktivieren. - Bitte warten Sie, während nach Schriftarten für Tastatur %0:s gesucht wird + Bitte warten Sie, während nach verwandten Schriftarten für Tastatur %1$s gesucht wird - Tastatur %0:s ist keine Unicode-Tastatur. Schriftarten können nicht automatisch gefunden werden. Bitte überprüfen Sie die Tastaturdokumentation für Schriftarten. + Tastatur %1$s ist keine Unicode-Tastatur. Schriftarten können nicht automatisch gefunden werden. Bitte überprüfen Sie die Tastaturdokumentation für Schriftarten. diff --git a/windows/src/desktop/setup/locale/de/strings.xml b/windows/src/desktop/setup/locale/de/strings.xml index 7622a24850..734cf0df7a 100644 --- a/windows/src/desktop/setup/locale/de/strings.xml +++ b/windows/src/desktop/setup/locale/de/strings.xml @@ -1,12 +1,12 @@ - Englisch + Deutsch $APPNAME $VERSION Setup Installiere $APPNAME $VERSION $APPNAME $VERSION wurde erfolgreich installiert. Sind Sie sicher, dass Sie die Installation von $APPNAME abbrechen möchten? Entpacke Dateien... - Pakete werden überprüft... + Pakete werden überprüft... Prüfe online auf Updates... Installierte Versionen werden überprüft... Fertigstellen... @@ -18,7 +18,7 @@ • %0:s %1:s für %2:s %3:s Es gibt nichts zu installieren. - (%0:s herunterladen) + (%0:s herunterladen) Setup installiert: $APPNAME $VERSION ist kostenlos und Open Source &Lizenzbedingungen @@ -56,7 +56,7 @@ Jetzt neu starten? $APPNAME %0:s ist bereits installiert. - Version %0:s herunterladen (%1:s) + Version %0:s herunterladen (%1:s) Version %0:s From 08257cf6330a52893befce40ece284294a992923 Mon Sep 17 00:00:00 2001 From: Keyman Build Agent Date: Wed, 3 Mar 2021 13:02:52 -0500 Subject: [PATCH 089/125] auto: increment beta version to 14.0.252 --- HISTORY.md | 13 ++++++++++++ VERSION.md | 2 +- .../web/input-processor/package-lock.json | 2 +- common/core/web/input-processor/package.json | 16 +++++++-------- .../web/keyboard-processor/package-lock.json | 2 +- .../core/web/keyboard-processor/package.json | 10 +++++----- .../core/web/tools/recorder/package-lock.json | 2 +- common/core/web/tools/recorder/package.json | 10 +++++----- .../tools/sentry-manager/package-lock.json | 2 +- .../web/tools/sentry-manager/package.json | 4 ++-- common/core/web/utils/package-lock.json | 2 +- common/core/web/utils/package.json | 6 +++--- common/models/templates/package-lock.json | 2 +- common/models/templates/package.json | 8 ++++---- common/models/types/package-lock.json | 2 +- common/models/types/package.json | 2 +- common/models/wordbreakers/package-lock.json | 2 +- common/models/wordbreakers/package.json | 4 ++-- common/predictive-text/package-lock.json | 2 +- common/predictive-text/package.json | 14 ++++++------- developer/js/package-lock.json | 2 +- developer/js/package.json | 8 ++++---- lerna.json | 2 +- resources/gosh/package-lock.json | 2 +- resources/gosh/package.json | 2 +- resources/web-environment/package-lock.json | 2 +- resources/web-environment/package.json | 4 ++-- web/package-lock.json | 2 +- web/package.json | 20 +++++++++---------- 29 files changed, 82 insertions(+), 69 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 6bb3b2d72a..944016731b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,18 @@ # Keyman Version History +## 14.0.251 beta 2021-03-03 + +* fix(developer): Improve stability of named code constants (#4547) +* fix(developer): schema conformance for model package compiler (#4548) +* fix(developer): touch layout osk import handling of multiple modifiers (#4552) +* fix(developer): require language tag when compiling keyboard package (#4563) +* fix(developer): Avoid blank keys when importing KMX to KVKS (#4564) +* feat(developer): isRTL support for lexical model editor (#4559) +* fix(developer): track modified state in wordlist editor better (#4562) +* chore(ios): better visual feedback for keyboard search during poor internet connectivity (#4573) +* chore(common): Update crowdin files for `de` (#4578) +* chore(common/core/desktop): write debug output to console (#4569) + ## 14.0.250 beta 2021-03-02 * fix(common/resources): Fix help.keyman.com path for CI (#4565) diff --git a/VERSION.md b/VERSION.md index 169475d9ee..b5db270f41 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -14.0.251 \ No newline at end of file +14.0.252 \ No newline at end of file diff --git a/common/core/web/input-processor/package-lock.json b/common/core/web/input-processor/package-lock.json index 606905be55..22e7596770 100644 --- a/common/core/web/input-processor/package-lock.json +++ b/common/core/web/input-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.251", + "version": "14.0.252", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/input-processor/package.json b/common/core/web/input-processor/package.json index 45b23e073e..16ad0e0238 100644 --- a/common/core/web/input-processor/package.json +++ b/common/core/web/input-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "14.0.251", + "version": "14.0.252", "description": "The core text and prediction processing engine for KeymanWeb", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/lexical-model-compiler": "^14.0.251", - "@keymanapp/resources-gosh": "^14.0.251", + "@keymanapp/lexical-model-compiler": "^14.0.252", + "@keymanapp/resources-gosh": "^14.0.252", "@types/node": "^11.9.4", "chai": "^4.2.0", "mocha": "^5.2.0", @@ -34,11 +34,11 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.251", - "@keymanapp/lexical-model-layer": "^14.0.251", - "@keymanapp/models-types": "^14.0.251", - "@keymanapp/web-environment": "^14.0.251", - "@keymanapp/web-utils": "^14.0.251", + "@keymanapp/keyboard-processor": "^14.0.252", + "@keymanapp/lexical-model-layer": "^14.0.252", + "@keymanapp/models-types": "^14.0.252", + "@keymanapp/web-environment": "^14.0.252", + "@keymanapp/web-utils": "^14.0.252", "eventemitter3": "^4.0.0" } } diff --git a/common/core/web/keyboard-processor/package-lock.json b/common/core/web/keyboard-processor/package-lock.json index 3918ab5b71..44aac66c7e 100644 --- a/common/core/web/keyboard-processor/package-lock.json +++ b/common/core/web/keyboard-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.251", + "version": "14.0.252", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/keyboard-processor/package.json b/common/core/web/keyboard-processor/package.json index 730aff8d8e..31d7f719b4 100644 --- a/common/core/web/keyboard-processor/package.json +++ b/common/core/web/keyboard-processor/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "14.0.251", + "version": "14.0.252", "description": "Core module for Keyman keyboard support in KeymanWeb.", "repository": { "type": "git", @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.251", + "@keymanapp/resources-gosh": "^14.0.252", "chai": "^4.2.0", "mocha": "^5.2.0", "mocha-teamcity-reporter": "^3.0.0", @@ -31,9 +31,9 @@ "mocha": "mocha" }, "dependencies": { - "@keymanapp/models-types": "^14.0.251", - "@keymanapp/web-environment": "^14.0.251", - "@keymanapp/web-utils": "^14.0.251", + "@keymanapp/models-types": "^14.0.252", + "@keymanapp/web-environment": "^14.0.252", + "@keymanapp/web-utils": "^14.0.252", "@types/node": "^11.9.4", "ts-node": "^8.0.2" } diff --git a/common/core/web/tools/recorder/package-lock.json b/common/core/web/tools/recorder/package-lock.json index 1fd6b9f20e..bc05bbfede 100644 --- a/common/core/web/tools/recorder/package-lock.json +++ b/common/core/web/tools/recorder/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.251", + "version": "14.0.252", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/recorder/package.json b/common/core/web/tools/recorder/package.json index 82daae28ec..46693a85a0 100644 --- a/common/core/web/tools/recorder/package.json +++ b/common/core/web/tools/recorder/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "14.0.251", + "version": "14.0.252", "description": "Core classes used to develop KeymanWeb test cases based on keystrokes", "main": "index.js", "scripts": { @@ -17,10 +17,10 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "dependencies": { - "@keymanapp/keyboard-processor": "^14.0.251", - "@keymanapp/models-types": "^14.0.251", - "@keymanapp/web-environment": "^14.0.251", - "@keymanapp/web-utils": "^14.0.251" + "@keymanapp/keyboard-processor": "^14.0.252", + "@keymanapp/models-types": "^14.0.252", + "@keymanapp/web-environment": "^14.0.252", + "@keymanapp/web-utils": "^14.0.252" }, "devDependencies": { "typescript": "^3.8.3" diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json index 8ad6572a32..3b25675373 100644 --- a/common/core/web/tools/sentry-manager/package-lock.json +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.251", + "version": "14.0.252", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json index 7c013e3671..4338d3059b 100644 --- a/common/core/web/tools/sentry-manager/package.json +++ b/common/core/web/tools/sentry-manager/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "14.0.251", + "version": "14.0.252", "description": "Common setup for KeymanWeb's Sentry-based error reporting", "main": "dist/index.js", "scripts": { @@ -20,7 +20,7 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/web-environment": "^14.0.251", + "@keymanapp/web-environment": "^14.0.252", "@sentry/browser": "^5.27.4" } } diff --git a/common/core/web/utils/package-lock.json b/common/core/web/utils/package-lock.json index 2125b91a9c..09525fdb21 100644 --- a/common/core/web/utils/package-lock.json +++ b/common/core/web/utils/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.251", + "version": "14.0.252", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/utils/package.json b/common/core/web/utils/package.json index d0ccc0bc10..4a9390740b 100644 --- a/common/core/web/utils/package.json +++ b/common/core/web/utils/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "14.0.251", + "version": "14.0.252", "description": "Common utility functions used throughout other Keyman packages", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -20,8 +20,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.251", - "@keymanapp/web-environment": "^14.0.251", + "@keymanapp/resources-gosh": "^14.0.252", + "@keymanapp/web-environment": "^14.0.252", "@types/node": "^14.0.5", "typescript": "^3.8.3" } diff --git a/common/models/templates/package-lock.json b/common/models/templates/package-lock.json index de988f7768..47952f0d94 100644 --- a/common/models/templates/package-lock.json +++ b/common/models/templates/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.251", + "version": "14.0.252", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/templates/package.json b/common/models/templates/package.json index 703e84670f..fb672e90e5 100644 --- a/common/models/templates/package.json +++ b/common/models/templates/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "14.0.251", + "version": "14.0.252", "description": "Backing model implementations (templates) for Keyman's modelling layer", "keywords": [ "lm", @@ -42,8 +42,8 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.251", - "@keymanapp/web-utils": "^14.0.251", + "@keymanapp/models-types": "^14.0.252", + "@keymanapp/web-utils": "^14.0.252", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "@types/node": "^14.0.4", @@ -52,6 +52,6 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-wordbreakers": "^14.0.251" + "@keymanapp/models-wordbreakers": "^14.0.252" } } diff --git a/common/models/types/package-lock.json b/common/models/types/package-lock.json index 3adf27f054..34e9a0eb1d 100644 --- a/common/models/types/package-lock.json +++ b/common/models/types/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.251", + "version": "14.0.252", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/types/package.json b/common/models/types/package.json index 6d18e1cd7c..3396d80d57 100644 --- a/common/models/types/package.json +++ b/common/models/types/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "14.0.251", + "version": "14.0.252", "description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.", "types": "./index.d.ts", "scripts": { diff --git a/common/models/wordbreakers/package-lock.json b/common/models/wordbreakers/package-lock.json index 03f67cbcd9..754b33f784 100644 --- a/common/models/wordbreakers/package-lock.json +++ b/common/models/wordbreakers/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.251", + "version": "14.0.252", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json index ec513d1cad..7fe8554c92 100644 --- a/common/models/wordbreakers/package.json +++ b/common/models/wordbreakers/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "14.0.251", + "version": "14.0.252", "description": "Word breakers bundled in the modeling layer", "keywords": [ "lexical model", @@ -40,7 +40,7 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/models-types": "^14.0.251", + "@keymanapp/models-types": "^14.0.252", "@types/chai": "^4.2.11", "@types/mocha": "^7.0.2", "chai": "^4.2.0", diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index bdda591363..a57dae50fc 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.251", + "version": "14.0.252", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json index 8b1f07dcd0..ea5d6c030e 100644 --- a/common/predictive-text/package.json +++ b/common/predictive-text/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "14.0.251", + "version": "14.0.252", "description": "Keyman/Predictive Text integration layer", "main": "build/index.js", "scripts": { @@ -21,8 +21,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/models-types": "^14.0.251", - "@keymanapp/resources-gosh": "^14.0.251", + "@keymanapp/models-types": "^14.0.252", + "@keymanapp/resources-gosh": "^14.0.252", "@types/node": "^10.17.21", "chai": "^4.2.0", "karma": "^4.2.0", @@ -46,10 +46,10 @@ "typescript": "^3.8.3" }, "dependencies": { - "@keymanapp/models-templates": "^14.0.251", - "@keymanapp/models-wordbreakers": "^14.0.251", - "@keymanapp/web-environment": "^14.0.251", - "@keymanapp/web-utils": "^14.0.251", + "@keymanapp/models-templates": "^14.0.252", + "@keymanapp/models-wordbreakers": "^14.0.252", + "@keymanapp/web-environment": "^14.0.252", + "@keymanapp/web-utils": "^14.0.252", "es6-shim": "^0.35.5", "string.prototype.codepointat": "^0.2.1", "string.prototype.startswith": "^0.2.0" diff --git a/developer/js/package-lock.json b/developer/js/package-lock.json index 927aefebd5..0a008e93d9 100644 --- a/developer/js/package-lock.json +++ b/developer/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.251", + "version": "14.0.252", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/developer/js/package.json b/developer/js/package.json index 950be10f75..80af445cc2 100644 --- a/developer/js/package.json +++ b/developer/js/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "14.0.251", + "version": "14.0.252", "description": "Keyman Developer lexical model compiler", "keywords": [ "keyboard", @@ -37,14 +37,14 @@ "node": ">=12.0.0" }, "dependencies": { - "@keymanapp/models-types": "^14.0.251", + "@keymanapp/models-types": "^14.0.252", "commander": "^3.0.0", "typescript": "^3.8.3", "xml2js": "^0.4.19" }, "devDependencies": { - "@keymanapp/models-templates": "^14.0.251", - "@keymanapp/models-wordbreakers": "^14.0.251", + "@keymanapp/models-templates": "^14.0.252", + "@keymanapp/models-wordbreakers": "^14.0.252", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", diff --git a/lerna.json b/lerna.json index 72e696c937..64ab6d13f0 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "lerna", "typescript" ], - "version": "14.0.251" + "version": "14.0.252" } diff --git a/resources/gosh/package-lock.json b/resources/gosh/package-lock.json index c431bbc703..ab715e6a30 100644 --- a/resources/gosh/package-lock.json +++ b/resources/gosh/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.251", + "version": "14.0.252", "lockfileVersion": 1 } diff --git a/resources/gosh/package.json b/resources/gosh/package.json index 4335896d44..d5470f54c4 100644 --- a/resources/gosh/package.json +++ b/resources/gosh/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/resources-gosh", - "version": "14.0.251", + "version": "14.0.252", "description": "Good Ol' Shell", "author": "Marc Durdin (https://github.com/mcdurdin)", "contributors": [ diff --git a/resources/web-environment/package-lock.json b/resources/web-environment/package-lock.json index 5f7ed75bdc..2f8b3c2992 100644 --- a/resources/web-environment/package-lock.json +++ b/resources/web-environment/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.251", + "version": "14.0.252", "lockfileVersion": 1 } diff --git a/resources/web-environment/package.json b/resources/web-environment/package.json index 975e8ae3d8..f7142754dc 100644 --- a/resources/web-environment/package.json +++ b/resources/web-environment/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-environment", - "version": "14.0.251", + "version": "14.0.252", "description": "A compilation utility for passing select environment variables into our TS builds", "author": "Joshua Horton (https://github.com/jahorton)", "homepage": "https://github.com/keymanapp/keyman", @@ -17,6 +17,6 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.251" + "@keymanapp/resources-gosh": "^14.0.252" } } diff --git a/web/package-lock.json b/web/package-lock.json index 3e710d06be..0b565d16c2 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.251", + "version": "14.0.252", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/web/package.json b/web/package.json index 2f761e88e0..4364339936 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "14.0.251", + "version": "14.0.252", "description": "Facilitates text input in any language.", "repository": { "type": "git", @@ -18,8 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "^14.0.251", - "@keymanapp/web-sentry-manager": "^14.0.251", + "@keymanapp/resources-gosh": "^14.0.252", + "@keymanapp/web-sentry-manager": "^14.0.252", "@sentry/cli": "^1.52.3", "chai": "^4.2.0", "google-closure-compiler-java": "^20200224.0.0", @@ -51,13 +51,13 @@ "sentry-cli": "sentry-cli" }, "dependencies": { - "@keymanapp/input-processor": "^14.0.251", - "@keymanapp/keyboard-processor": "^14.0.251", - "@keymanapp/lexical-model-layer": "^14.0.251", - "@keymanapp/models-types": "^14.0.251", - "@keymanapp/recorder-core": "^14.0.251", - "@keymanapp/web-environment": "^14.0.251", - "@keymanapp/web-utils": "^14.0.251", + "@keymanapp/input-processor": "^14.0.252", + "@keymanapp/keyboard-processor": "^14.0.252", + "@keymanapp/lexical-model-layer": "^14.0.252", + "@keymanapp/models-types": "^14.0.252", + "@keymanapp/recorder-core": "^14.0.252", + "@keymanapp/web-environment": "^14.0.252", + "@keymanapp/web-utils": "^14.0.252", "@types/node": "^11.9.4", "es6-shim": "^0.35.5", "eventemitter3": "^4.0.0", From 0158e17636057cc7252703d9482f86593a089148 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Wed, 3 Mar 2021 19:37:02 +0100 Subject: [PATCH 090/125] chore(linux): Don't report to Sentry in dev environment When the environment variable KEYMAN_NOSENTRY is set we'll skip reporting to Sentry. This is helpful on development machines. --- linux/keyman-config/keyman_config/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux/keyman-config/keyman_config/__init__.py b/linux/keyman-config/keyman_config/__init__.py index 4a1b314b87..cbbce7a3d9 100644 --- a/linux/keyman-config/keyman_config/__init__.py +++ b/linux/keyman-config/keyman_config/__init__.py @@ -36,6 +36,8 @@ KeymanDownloadsUrl = 'https://downloads.keyman.com' if 'unittest' in sys.modules.keys(): print('Not reporting to Sentry') +elif os.environ.get('KEYMAN_NOSENTRY'): + print('Not reporting to Sentry because KEYMAN_NOSENTRY environment variable set') else: try: # Try new sentry-sdk first From 33bf5750b05834ca6a2f4beb3eb12e0795411f76 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Wed, 3 Mar 2021 19:13:20 +0100 Subject: [PATCH 091/125] fix(linux): Improve version number This change modifies the reported version number for km-config: if km-config is installed from a debian package, the package version will be output as well, e.g.: `km-config version 14.0.252-beta (package version 14.0.252-1+bionic1)` Otherwise the output will be identical to before: `km-config version 14.0.252-beta-local`. This change also modifies the script that sets the version number so that builds on Jenkins are now detected in addition to TC. This fixes #4579. --- linux/.gitignore | 7 +++++++ linux/keyman-config/debian/rules | 1 + linux/keyman-config/keyman_config/__init__.py | 1 + linux/keyman-config/keyman_config/version.py.in | 1 + linux/keyman-config/km-config | 8 ++++++-- linux/scripts/launchpad.sh | 1 - resources/build/build-utils.sh | 4 ++-- 7 files changed, 18 insertions(+), 5 deletions(-) diff --git a/linux/.gitignore b/linux/.gitignore index f559eeae9d..3bfb0eb6f6 100644 --- a/linux/.gitignore +++ b/linux/.gitignore @@ -90,3 +90,10 @@ keyman-config/keyman_config/version.py keyman-config/locale/*/LC_MESSAGES/ test.sh debianpackage/ +*.deb +*.build +*.buildinfo +*.changes +*.tar.xz +*.tar.gz +*.dsc \ No newline at end of file diff --git a/linux/keyman-config/debian/rules b/linux/keyman-config/debian/rules index bde563844e..8b4c7082af 100755 --- a/linux/keyman-config/debian/rules +++ b/linux/keyman-config/debian/rules @@ -9,6 +9,7 @@ export PYBUILD_INSTALL_ARGS=--install-scripts=/usr/share/keyman-config/ override_dh_auto_build: make man + sed -i -e "s/^__pkgversion__ = \"\"/__pkgversion__ = \"$(shell dpkg-parsechangelog --show-field Version)\"/g" keyman_config/version.py make compile-po dh_auto_build $@ diff --git a/linux/keyman-config/keyman_config/__init__.py b/linux/keyman-config/keyman_config/__init__.py index 4a1b314b87..f75cac273c 100644 --- a/linux/keyman-config/keyman_config/__init__.py +++ b/linux/keyman-config/keyman_config/__init__.py @@ -9,6 +9,7 @@ from .version import __versionwithtag__ from .version import __majorversion__ from .version import __releaseversion__ from .version import __tier__ +from .version import __pkgversion__ def _(txt): diff --git a/linux/keyman-config/keyman_config/version.py.in b/linux/keyman-config/keyman_config/version.py.in index c2970be4be..366071faa5 100644 --- a/linux/keyman-config/keyman_config/version.py.in +++ b/linux/keyman-config/keyman_config/version.py.in @@ -9,3 +9,4 @@ __versionwithtag__ = "_VERSIONWITHTAG_" __majorversion__ = "_MAJORVERSION_" __releaseversion__ = "_RELEASEVERSION_" __tier__ = "_TIER_" +__pkgversion__ = "" diff --git a/linux/keyman-config/km-config b/linux/keyman-config/km-config index 0f0bfeae46..172e484055 100755 --- a/linux/keyman-config/km-config +++ b/linux/keyman-config/km-config @@ -3,13 +3,17 @@ import argparse import logging -from keyman_config import __versionwithtag__ +from keyman_config import __versionwithtag__, __pkgversion__ from keyman_config.handle_install import download_and_install_package if __name__ == '__main__': + if __pkgversion__: + versionstring = "%s (package version %s)" % (__versionwithtag__, __pkgversion__) + else: + versionstring = "%s" % __versionwithtag__ parser = argparse.ArgumentParser(description='Keyman keyboards installation and information') - parser.add_argument('--version', action='version', version='%(prog)s version ' + __versionwithtag__) + parser.add_argument('--version', action='version', version='%(prog)s version ' + versionstring) parser.add_argument('-v', '--verbose', action='store_true', help='verbose logging') parser.add_argument('-vv', '--veryverbose', action='store_true', help='very verbose logging') parser.add_argument('-i', '--install', action='store', nargs='?', help='install .kmp package') diff --git a/linux/scripts/launchpad.sh b/linux/scripts/launchpad.sh index f7a9b75448..351cb73fa8 100755 --- a/linux/scripts/launchpad.sh +++ b/linux/scripts/launchpad.sh @@ -61,7 +61,6 @@ for proj in ${projects}; do cd ${proj}-${version} echo $(pwd) cp debian/changelog ../${proj}-changelog - #TODO separate source builds and dputs for each of $dists? for dist in ${distributions}; do cp ../${proj}-changelog debian/changelog dch -v ${version}-${packageversion}~${dist} "source package for PPA" diff --git a/resources/build/build-utils.sh b/resources/build/build-utils.sh index 0211dd14af..a6696402aa 100755 --- a/resources/build/build-utils.sh +++ b/resources/build/build-utils.sh @@ -73,12 +73,12 @@ function findVersion() { VERSION_TAG= fi - if [ -z "${TEAMCITY_VERSION-}" ]; then + if [ -z "${TEAMCITY_VERSION-}" -a -z "${JENKINS_HOME-}" ]; then # Local dev machine, not TeamCity VERSION_TAG="$VERSION_TAG-local" VERSION_ENVIRONMENT=local else - # On TeamCity; are we running a pull request build or a master/beta/stable build? + # On TeamCity: are we running a pull request build or a master/beta/stable build? if [ ! -z "${TEAMCITY_PR_NUMBER-}" ]; then VERSION_ENVIRONMENT=test # Note TEAMCITY_PR_NUMBER can also be 'master', 'beta', or 'stable-x.y' From fe5de67c1fe505d2eb5b3d74a2f1fd5dab2e8180 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 4 Mar 2021 09:43:30 +1100 Subject: [PATCH 092/125] feat(developer): Range expansions Relates to #2241. Range expansions are a new language feature that reduce verbosity in Keyman Keyboard Language (.kmn) files by making it possible to collapse sequential ranges with a new `..` operator. It is envisioned that these will be used primarily in stores. Two kinds of expansions are available: character ranges, and virtual key ranges. The syntax is: ``` char '..' char vkey '..' vkey ``` Where the `char` terminator is a single normal character, and the `vkey` terminator is a single virtual key. `outs` is permissible for terminators but no other statement is allowed. Whitespace is per normal Keyman keyboard language syntax. Character ranges will replace the range with the set of characters between the terminators, based on the Unicode value of the terminators. Examples: ``` store(alphabet) 'a' .. 'z' store(alpha_not_bq) 'ac'..'pr'..'z' store(capitals) U+0041 .. U+005A ``` Virtual key ranges work in a similar fashion, but with virtual keys. The starting and terminating virtual key must have the same shift state. This will be most useful for character and numeric virtual keys, as the ranges are based on the numeric value of the virtual key as defined by Windows, not its position on the keyboard. Examples: ``` store(alphakeys) [K_A] .. [K_Z] store(numkeys) [K_0] .. [K_9] ``` Ranges must be positive, that is, you cannot use `'z' .. 'a'`. While this expansion is not expected to be very useful in rules, it is still permitted. `context()` and other offsets are calculated on the expanded form, not the collapsed form. Similarly, `any()`/`index()` offsets are calculated on the expanded form. 'a' .. 'z' > context(3) c this produces 'c', not 'z' Because ranges are expanded at compile time, ranges that are overly long will result in an error, so you cannot use, for example: ``` store(AllUnicode) U+0020 .. U+10FFFF ``` The precise maximum length of a range is dependent on the context where it is used. The changes are entirely compiler-based; no change is made to the .kmx file format or to the Keyman apps. --- windows/src/developer/kmcmpdll/Compiler.cpp | 107 ++++++++++++++++++ windows/src/developer/kmcmpdll/Compiler.rc | 9 ++ .../delphi/general/CompileErrorCodes.pas | 6 + windows/src/global/inc/Comperr.h | 6 + windows/src/global/inc/unicode.h | 3 +- windows/src/global/vc/xstring.cpp | 2 +- windows/src/test/unit-tests/kmcomp/test.bat | 7 ++ .../test/unit-tests/kmcomp/test_expansion.bmp | 0 .../test/unit-tests/kmcomp/test_expansion.kmn | Bin 0 -> 728 bytes .../unit-tests/kmcomp/test_expansion_1.kmn | 14 +++ .../kmcomp/test_expansion_invalid.kmn | 34 ++++++ windows/src/test/unit-tests/kmcomp/tests.kpj | 38 ++++++- 12 files changed, 220 insertions(+), 6 deletions(-) create mode 100644 windows/src/test/unit-tests/kmcomp/test_expansion.bmp create mode 100644 windows/src/test/unit-tests/kmcomp/test_expansion.kmn create mode 100644 windows/src/test/unit-tests/kmcomp/test_expansion_1.kmn create mode 100644 windows/src/test/unit-tests/kmcomp/test_expansion_invalid.kmn diff --git a/windows/src/developer/kmcmpdll/Compiler.cpp b/windows/src/developer/kmcmpdll/Compiler.cpp index 444c1126ef..c4c79216a8 100644 --- a/windows/src/developer/kmcmpdll/Compiler.cpp +++ b/windows/src/developer/kmcmpdll/Compiler.cpp @@ -135,6 +135,7 @@ DWORD process_save(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx); DWORD process_platform(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx); // I3430 DWORD process_baselayout(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx); // I3430 DWORD process_set_synonym(DWORD dwSystemID, PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx); // I3437 +DWORD process_expansion(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx, int max); BOOL IsValidKeyboardVersion(WCHAR *dpString); // I4140 @@ -1957,6 +1958,7 @@ DWORD GetXString(PFILE_KEYBOARD fk, PWSTR str, PWSTR token, PWSTR output, int ma case '$': type = 16; break; // named code constants case 'P': type = 17; break; // platform (synonym for if(&platform)) // I3430 case 'L': type = 18; break; // layer (synonym for set(&layer)) // I3437 + case '.': type = 19; break; // .. allows us to specify ranges -- either vkeys or characters default: if (iswdigit(*p)) type = 0; // octal number else type = 99; // error! @@ -2494,6 +2496,13 @@ DWORD GetXString(PFILE_KEYBOARD fk, PWSTR str, PWSTR token, PWSTR output, int ma err = process_set_synonym(TSS_LAYER, fk, q, tstr, &mx); if (err != CERR_None) return err; continue; + case 19: // #2241 + if (*(p + 1) != '.') return CERR_InvalidToken; + if (sFlag) return CERR_InvalidInVirtualKeySection; + p += 2; + err = process_expansion(fk, p, tstr, &mx, max); + if (err != CERR_None) return err; + continue; default: return CERR_InvalidToken; } @@ -2663,6 +2672,104 @@ DWORD process_reset(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx) return CERR_None; } +DWORD process_expansion(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx, int max) { + BOOL isVKey = FALSE; + + WORD BaseKey=0, BaseShiftFlags=0; + DWORD BaseChar=0; + + if (*mx == 0) { + return CERR_ExpansionMustFollowCharacterOrVKey; + } + LPWSTR p = &tstr[*mx]; + p = decxstr(p, tstr); + if (*p == UC_SENTINEL) { + if (*(p + 1) != CODE_EXTENDED) { + return CERR_ExpansionMustFollowCharacterOrVKey; + } + isVKey = TRUE; + BaseKey = *(p + 3); + BaseShiftFlags = *(p + 2); + } + else { + BaseChar = Uni_UTF16ToUTF32(p); + } + + // Look ahead at next element + WCHAR temp[64]; + PWCHAR r = NULL; + + DWORD msg; + + if ((msg = GetXString(fk, q, L"", temp, _countof(temp) - 1, 0, &r, FALSE, TRUE)) != CERR_None) + { + return msg; + } + + WORD HighKey, HighShiftFlags; + DWORD HighChar; + + switch(temp[0]) { + case 0: + return isVKey ? CERR_VKeyExpansionMustBeFollowedByVKey : CERR_CharacterExpansionMustBeFollowedByCharacter; + case UC_SENTINEL: + // Verify that range is valid virtual key range + if(!isVKey) { + return CERR_CharacterExpansionMustBeFollowedByCharacter; + } + if (temp[1] != CODE_EXTENDED) { + return CERR_VKeyExpansionMustBeFollowedByVKey; + } + HighKey = temp[3], HighShiftFlags = temp[2]; + if (HighShiftFlags != BaseShiftFlags) { + return CERR_VKeyExpansionMustUseConsistentShift; + } + if (HighKey <= BaseKey) { + return CERR_ExpansionMustBePositive; + } + // Verify space in buffer + if (*mx + (HighKey - BaseKey) * 5 + 1 >= max) return CERR_BufferOverflow; + // Inject an expansion. + for (BaseKey++; BaseKey < HighKey; BaseKey++) { + // < HighKey because caller will add HighKey to output + tstr[(*mx)++] = UC_SENTINEL; + tstr[(*mx)++] = CODE_EXTENDED; + tstr[(*mx)++] = BaseShiftFlags; + tstr[(*mx)++] = BaseKey; + tstr[(*mx)++] = UC_SENTINEL_EXTENDEDEND; + } + tstr[*mx] = 0; + break; + default: + // Verify that range is a valid character range + if (isVKey) { + return CERR_VKeyExpansionMustBeFollowedByVKey; + } + + HighChar = Uni_UTF16ToUTF32(temp); + if (HighChar <= BaseChar) { + return CERR_ExpansionMustBePositive; + } + // Inject an expansion. + for (BaseChar++; BaseChar < HighChar; BaseChar++) { + // < HighChar because caller will add HighChar to output + if (Uni_IsSMP(BaseChar)) { + // We'll test on each char to avoid complex calculations crossing SMP boundary + if (*mx + 3 >= max) return CERR_BufferOverflow; + tstr[(*mx)++] = (WCHAR) Uni_UTF32ToSurrogate1(BaseChar); + tstr[(*mx)++] = (WCHAR) Uni_UTF32ToSurrogate2(BaseChar); + } + else { + if (*mx + 2 >= max) return CERR_BufferOverflow; + tstr[(*mx)++] = (WCHAR) BaseChar; + } + } + tstr[*mx] = 0; + } + + return CERR_None; +} + DWORD process_set_synonym(DWORD dwSystemID, PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx) // I3437 { /* set( <'='> ), layer */ diff --git a/windows/src/developer/kmcmpdll/Compiler.rc b/windows/src/developer/kmcmpdll/Compiler.rc index 78db3a3a20..f072c02d7a 100644 --- a/windows/src/developer/kmcmpdll/Compiler.rc +++ b/windows/src/developer/kmcmpdll/Compiler.rc @@ -228,3 +228,12 @@ BEGIN CWARN_NulNotFirstStatementInContext "nul must be the first statement in the context" CWARN_IfShouldBeAtStartOfContext "if, platform and baselayout should be at start of context (after nul, if present])" END + +STRINGTABLE +BEGIN + CERR_ExpansionMustFollowCharacterOrVKey "An expansion must follow a character or a virtual key" + CERR_VKeyExpansionMustBeFollowedByVKey "A virtual key expansion must be terminated by a virtual key" + CERR_CharacterExpansionMustBeFollowedByCharacter "A character expansion must be terminated by a character key" + CERR_VKeyExpansionMustUseConsistentShift "A virtual key expansion must use the same shift state for both terminators" + CERR_ExpansionMustBePositive "An expansion must have positive difference (i.e. A-Z, not Z-A)" +END \ No newline at end of file diff --git a/windows/src/global/delphi/general/CompileErrorCodes.pas b/windows/src/global/delphi/general/CompileErrorCodes.pas index 43f9c935ca..b1a58252f1 100644 --- a/windows/src/global/delphi/general/CompileErrorCodes.pas +++ b/windows/src/global/delphi/general/CompileErrorCodes.pas @@ -147,6 +147,12 @@ const CERR_ContextAndIndexInvalidInMatchNomatch = $4062; CERR_140FeatureOnlyContextAndNotAnyWeb = $4063; + CERR_ExpansionMustFollowCharacterOrVKey = $4064; + CERR_VKeyExpansionMustBeFollowedByVKey = $4065; + CERR_CharacterExpansionMustBeFollowedByCharacter = $4066; + CERR_VKeyExpansionMustUseConsistentShift = $4067; + CERR_ExpansionMustBePositive = $4068; + CWARN_TooManyWarnings = $2080; CWARN_OldVersion = $2081; CWARN_BitmapNotUsed = $2082; diff --git a/windows/src/global/inc/Comperr.h b/windows/src/global/inc/Comperr.h index 09af93c431..4ac2f5fb8f 100644 --- a/windows/src/global/inc/Comperr.h +++ b/windows/src/global/inc/Comperr.h @@ -143,6 +143,12 @@ #define CERR_ContextAndIndexInvalidInMatchNomatch 0x00004062 #define CERR_140FeatureOnlyContextAndNotAnyWeb 0x00004063 +#define CERR_ExpansionMustFollowCharacterOrVKey 0x00004064 +#define CERR_VKeyExpansionMustBeFollowedByVKey 0x00004065 +#define CERR_CharacterExpansionMustBeFollowedByCharacter 0x00004066 +#define CERR_VKeyExpansionMustUseConsistentShift 0x00004067 +#define CERR_ExpansionMustBePositive 0x00004068 + #define CWARN_TooManyWarnings 0x00002080 #define CWARN_OldVersion 0x00002081 #define CWARN_BitmapNotUsed 0x00002082 diff --git a/windows/src/global/inc/unicode.h b/windows/src/global/inc/unicode.h index 84dd71f565..e7edaaf68a 100644 --- a/windows/src/global/inc/unicode.h +++ b/windows/src/global/inc/unicode.h @@ -11,9 +11,10 @@ #define Uni_UTF32ToSurrogate1(ch) (((ch) - 0x10000) / 0x400 + 0xD800) #define Uni_UTF32ToSurrogate2(ch) (((ch) - 0x10000) % 0x400 + 0xDC00) +#define Uni_UTF16ToUTF32(p) (Uni_IsSurrogate1(*(p)) && Uni_IsSurrogate2(*((p)+1)) ? Uni_SurrogateToUTF32(*(p), *((p)+1)) : *(p)) int wcssuppos(PWSTR p1, PWSTR p); // return p-p1, surr pairs as one chr -int GetSuppChar(WCHAR *p); // return *p, + *(++p) if surr pair) +int GetSuppChar(WCHAR *p); // return *p, + *(++p) if surr pair), equivalent to Uni_UTF16ToUTF32 int wcspos(PWSTR s, int pos); // return pos, inc for s[xx] is surr. PWSTR wcschrsupp(PWSTR buf, int x); WCHAR ByteToWChar(char b); diff --git a/windows/src/global/vc/xstring.cpp b/windows/src/global/vc/xstring.cpp index f729f3d36a..3d1807c5be 100644 --- a/windows/src/global/vc/xstring.cpp +++ b/windows/src/global/vc/xstring.cpp @@ -47,7 +47,7 @@ PWSTR incxstr(PWSTR p) case CODE_INDEX: return p+2; case CODE_USE: return p+1; case CODE_DEADKEY: return p+1; - case CODE_EXTENDED: p += 2; while(*p != UC_SENTINEL_EXTENDEDEND) p++; return p+1; + case CODE_EXTENDED: p += 2; while(*p && *p != UC_SENTINEL_EXTENDEDEND) p++; return p+1; case CODE_CLEARCONTEXT: return p+1; case CODE_CALL: return p+1; case CODE_CONTEXTEX: return p+1; diff --git a/windows/src/test/unit-tests/kmcomp/test.bat b/windows/src/test/unit-tests/kmcomp/test.bat index ecf6ed87c7..0c580bd9f0 100644 --- a/windows/src/test/unit-tests/kmcomp/test.bat +++ b/windows/src/test/unit-tests/kmcomp/test.bat @@ -39,6 +39,13 @@ call :should-fail "#4280: nul should be at start of context 2" test_4280_nul_sta call :should-pass "#4423: named code constants, various tests" test_namedcodeconstants.kmn || goto :eof +call :should-pass "#2241: expansions" test_expansion.kmn || goto :eof +call :should-pass "#2241: expansions" test_expansion_1.kmn || goto :eof +:: the two files should be identical. +fc /b test_expansion.kmx test_expansion_1.kmx || goto :eof + +call :should-fail "#2241: invalid expansions" test_expansion_invalid.kmn || goto :eof + goto :eof :should-pass diff --git a/windows/src/test/unit-tests/kmcomp/test_expansion.bmp b/windows/src/test/unit-tests/kmcomp/test_expansion.bmp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/windows/src/test/unit-tests/kmcomp/test_expansion.kmn b/windows/src/test/unit-tests/kmcomp/test_expansion.kmn new file mode 100644 index 0000000000000000000000000000000000000000..cdb114814ce999d71eb9f4f2270a939763f21619 GIT binary patch literal 728 zcma))K~KU^5QOK65$w&s5s!c&@BxLzaWjbV2RfY6_*~ z3oStt#q=y;@SG)a16I*7*H%57R~pwqXL{CHPkPXhx;`3%9tYCZ(5L&IiWIJb`Ux?! zX@08HEw|T`Xu$l)s)qFR=%`S#MpN_6q51@Uwbgr1b{b>zrP34*?@0=OP#dAx>5|K9 z|K@IQx1e&{>3h{9*jR7--hd<^UTbr9&368)bf-^ru92?n8ts{y=*>x7*S1TuZ@1Au k=9${Mj;WfW-2YcOI|qN_mOrx-{5j+Z&fImpx;L->0PnnaJ^%m! literal 0 HcmV?d00001 diff --git a/windows/src/test/unit-tests/kmcomp/test_expansion_1.kmn b/windows/src/test/unit-tests/kmcomp/test_expansion_1.kmn new file mode 100644 index 0000000000..e10fabaf28 --- /dev/null +++ b/windows/src/test/unit-tests/kmcomp/test_expansion_1.kmn @@ -0,0 +1,14 @@ +c This file is the baseline for test_expansion; the two files should compile to be identical +store(&NAME) 'Test Expansions' +begin Unicode > use(main) + +group(main) using keys + +store(char_range) 'abcdefghijklmnopqrstuvwxyz' +store(key_range) [K_A] [K_B] [K_C] [K_D] [K_E] + +store(schar_range) U+10001 U+10002 U+10003 U+10004 U+10005 U+10006 +store(schar_range2) U+1000 U+1001 U+1002 U+1003 U+1004 U+1005 U+1006 + +store(char_range_silly) 'cd' +store(key_range_silly) [K_C] [K_D] \ No newline at end of file diff --git a/windows/src/test/unit-tests/kmcomp/test_expansion_invalid.kmn b/windows/src/test/unit-tests/kmcomp/test_expansion_invalid.kmn new file mode 100644 index 0000000000..b721946537 --- /dev/null +++ b/windows/src/test/unit-tests/kmcomp/test_expansion_invalid.kmn @@ -0,0 +1,34 @@ +c This file tests that invalid expansions are caught +store(&NAME) 'Test Invalid Expansions' +begin Unicode > use(main) + +group(main) using keys + +c Backward ranges + +store(char_range_backward) 'c' .. 'a' +store(key_range_backward) [K_E] .. [K_A] + +store(char_range_empty) 'c' .. 'c' +store(key_range_empty) [K_E] .. [K_E] + +c Incomplete ranges + +store(range_open) .. +store(char_range_open_end) 'a' .. +store(char_range_open_start) .. 'z' + +store(key_range_open_end) [K_A] .. +store(key_range_open_start) .. [K_Z] + +c Inconsistent ranges + +store(char_range_to_key) 'a' .. [K_Z] +store(key_range_to_char) [K_A] .. 'z' + +store(char_range_to_statement) 'a' .. nul +store(key_range_to_statement) [K_A] .. nul +store(char_range_from_statement) nul .. 'a' +store(key_range_from_statement) nul .. [K_A] + +store(key_range_shift_inconsistent) [SHIFT K_A] .. [K_Z] diff --git a/windows/src/test/unit-tests/kmcomp/tests.kpj b/windows/src/test/unit-tests/kmcomp/tests.kpj index 14b42c913b..a5ae166c63 100644 --- a/windows/src/test/unit-tests/kmcomp/tests.kpj +++ b/windows/src/test/unit-tests/kmcomp/tests.kpj @@ -118,6 +118,14 @@ .kmn
+ + id_068020cc7b6376ed155d5b9fc0215cc6 + test_namedcodeconstants.kmn + test_namedcodeconstants.kmn + 1.0 + .kmn +
+ id_5f9e583f7392151b3c3a7d4577b31a4f test_valid.kmx @@ -143,12 +151,34 @@ id_98467bd680620f99f9adeb5347541b6a - id_068020cc7b6376ed155d5b9fc0215cc6 - test_namedcodeconstants.kmn - test_namedcodeconstants.kmn + id_a45739f276db90d247a6b60b0faa281c + test_expansion.kmn + test_expansion.kmn 1.0 .kmn -
+
+ Test Expansions +
+ + + id_103775d19d3ec8272e57e2e103960589 + test_expansion_1.kmn + test_expansion_1.kmn + 1.0 + .kmn +
+ Test Expansions +
+
+ + id_bb17cc25a8735482bba0fc34223c9fe8 + test_expansion_invalid.kmn + test_expansion_invalid.kmn + 1.0 + .kmn +
+ Test Invalid Expansions +
From 7e424aaa6299d321d8aa46b0ced259b331b81ef3 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 4 Mar 2021 10:12:24 +1100 Subject: [PATCH 093/125] chore(developer): tweak expansion test --- .../test/unit-tests/kmcomp/test_expansion.kmn | Bin 728 -> 800 bytes .../test/unit-tests/kmcomp/test_expansion_1.kmn | 1 + 2 files changed, 1 insertion(+) diff --git a/windows/src/test/unit-tests/kmcomp/test_expansion.kmn b/windows/src/test/unit-tests/kmcomp/test_expansion.kmn index cdb114814ce999d71eb9f4f2270a939763f21619..f22417c7c28c72091269017129eb37e957b624a1 100644 GIT binary patch delta 64 zcmcb?x`1s%1*35yLk>d$Lk2@4Lp(zsLq0# JCo`5Z0szkl4441_ delta 12 TcmZ3$c7t_81>@!ej0ubYAbbRZ diff --git a/windows/src/test/unit-tests/kmcomp/test_expansion_1.kmn b/windows/src/test/unit-tests/kmcomp/test_expansion_1.kmn index e10fabaf28..ced7ebf23b 100644 --- a/windows/src/test/unit-tests/kmcomp/test_expansion_1.kmn +++ b/windows/src/test/unit-tests/kmcomp/test_expansion_1.kmn @@ -5,6 +5,7 @@ begin Unicode > use(main) group(main) using keys store(char_range) 'abcdefghijklmnopqrstuvwxyz' +store(alpha_not_bq) 'acdefghijklmnoprstuvwxyz' store(key_range) [K_A] [K_B] [K_C] [K_D] [K_E] store(schar_range) U+10001 U+10002 U+10003 U+10004 U+10005 U+10006 From 49131de2c690df4b2d1d4f002b631618399fbc5f Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 4 Mar 2021 10:15:53 +1100 Subject: [PATCH 094/125] chore: fixup encoding --- .../test/unit-tests/kmcomp/test_expansion.kmn | Bin 800 -> 399 bytes windows/src/test/unit-tests/kmcomp/tests.kpj | 48 +++++++++--------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/windows/src/test/unit-tests/kmcomp/test_expansion.kmn b/windows/src/test/unit-tests/kmcomp/test_expansion.kmn index f22417c7c28c72091269017129eb37e957b624a1..0aba85d90c1ed5f98f6aaabfebe1dd1678902449 100644 GIT binary patch literal 399 zcmYk0v2Md45QcZ2;-(U-mF0BESSe~VRq2#Wm4z<&VxwRnK%%-&Uo0Cp3p(`w_uVJq z(5V1tr3oU17$9~SfqopCpsWdip5TsJH7{@r%CvCl8x~!gHaNYQtG@8oJ?q%o%`vYE8WYpDFWa!0+5SJE{fvo6hLnde2Hz z>baJnsS-F#6ihSY+=1mVCfcj#^D@`!>qL(l=|OiI&==yd^b>$|J%q4d>6oImqJPXe z^Jz9U&^5RDN!4e5>|FymJs3Gows>m3X}eGHS9rf=Qh7`~ms}AVmdOJIiV9>818iA}NUN+}xocCHr@~ zTa0z8p)ULy)=EwE>R(!yzEzV~CG&@w(xI.kmn
- - id_5f9e583f7392151b3c3a7d4577b31a4f - test_valid.kmx - test_valid.kmx - - .kmx - id_03dbd77eff6b6b923de6983fab2bd9d0 - - - id_416e28ba6ecd3550e599b16a2c37e864 - test_invalid_kmx.kmx - test_invalid_kmx.kmx - - .kmx - id_98467bd680620f99f9adeb5347541b6a - - - id_ee200303f52275647138898037a80e0e - test.doc - test.doc - - .doc - id_98467bd680620f99f9adeb5347541b6a - id_a45739f276db90d247a6b60b0faa281c test_expansion.kmn @@ -180,5 +156,29 @@ Test Invalid Expansions
+ + id_5f9e583f7392151b3c3a7d4577b31a4f + test_valid.kmx + test_valid.kmx + + .kmx + id_03dbd77eff6b6b923de6983fab2bd9d0 + + + id_416e28ba6ecd3550e599b16a2c37e864 + test_invalid_kmx.kmx + test_invalid_kmx.kmx + + .kmx + id_98467bd680620f99f9adeb5347541b6a + + + id_ee200303f52275647138898037a80e0e + test.doc + test.doc + + .doc + id_98467bd680620f99f9adeb5347541b6a + From 00515dee8e1dfad1327c26d924d3b55b53c777f0 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 4 Mar 2021 08:56:35 +0700 Subject: [PATCH 095/125] change(ios): attempt at changing file encoding of localizable.strings --- ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj | 2 +- ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 61f829145b..a0da6bcd81 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -510,7 +510,7 @@ CE9E95CC24CE786900F6DD78 /* UniversalLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniversalLinks.swift; sourceTree = ""; }; CE9E95CE24CE7A2C00F6DD78 /* UniversalLinkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniversalLinkTests.swift; sourceTree = ""; }; CE9ECB1D24D3A8CC007C5718 /* PackageInstallView_iPad.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PackageInstallView_iPad.xib; sourceTree = ""; }; - CEA1486E2407808F00C6ECD2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + CEA1486E2407808F00C6ECD2 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; CEA14871240780E100C6ECD2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ResourceInfoView.xib; sourceTree = ""; }; CEA14874240780EF00C6ECD2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/ResourceInfoView.strings; sourceTree = ""; }; CEA148772407869200C6ECD2 /* km */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = km; path = km.lproj/ResourceInfoView.strings; sourceTree = ""; }; diff --git a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj index bf008e8cb0..dc5b041499 100644 --- a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj +++ b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj @@ -317,7 +317,7 @@ CE002CB42408B4A5002026CE /* Sentry.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sentry.framework; path = ../../Carthage/Build/iOS/Sentry.framework; sourceTree = ""; }; CE1F5ECD23331DA400141F3E /* OfflineHelp.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = OfflineHelp.bundle; sourceTree = ""; }; CE2B1E4B21B6112B007D092E /* DeviceKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DeviceKit.framework; path = ../../Carthage/Build/iOS/DeviceKit.framework; sourceTree = ""; }; - CE4C654524D0020900070856 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + CE4C654524D0020900070856 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; CE6137FF1FB99538009D0EF2 /* KeymanEngine.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KeymanEngine.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CE6138031FB999C8009D0EF2 /* KeymanEngine.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KeymanEngine.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CE79CDB42370111200010C06 /* Themes+Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Themes+Colors.xcassets"; sourceTree = ""; }; From aba0a53b4e28e8c1a37aed5ffd19c395be4b5c7c Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 4 Mar 2021 09:07:41 +0700 Subject: [PATCH 096/125] chore(ios): because xcode's encoding converter is broken --- .../KeymanEngine/en.lproj/Localizable.strings | Bin 19782 -> 9890 bytes .../Keyman/en.lproj/Localizable.strings | Bin 6076 -> 3037 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/en.lproj/Localizable.strings b/ios/engine/KMEI/KeymanEngine/en.lproj/Localizable.strings index 8214a9a745c011a17ef6b632dcea8a52ca668720..408de2f755118206cb42c74a7653effb46ab7402 100644 GIT binary patch literal 9890 zcmd5?S&!Su5q|ftXb5Y}coB{d1bJ`}#N%TV!_L~s*vX4uT4awz4Asr$F|z)BzB;-| z>X?z%vp^mUYsBuZ>aVV^ihBC9xip1q^QNr3^46J^Yg@Z;rf*$gu0vy7(}boq>!tIi zTRLOw+LW!aRpV^2G4sCNm?Erw6>L%Zh57mEqnWK-(`E77jE~4@<;?ue%q|aXy_o&= z(W9q#D#TjYB+p#ivT6^DEL?{LEc6{+!-PEVn+7*4`i7K^DjGa1ecRcpvRxT`ROL(A zRgPh>W|kI{R9DP^0j_yidPkdC_*=zCz|?Dw&t5q18e8d!b{_gJ0zcp>Fb=2km5#*B z2i&i7jdz{NgZD0{Ymy;>5&i%qgCHNWVNEsy@lEKf!uZe`7(ZW{4{o!v-n6c{bnW!(6L`XGL~D>S!0 zch%V*NwnXPRl~Kh2r}P~T+Y-@$PssDU3N=aw6Ztko%z!Gg%kgdiiikSEg|i<2aehjI+9^V50jv8pF4wca(EUgyO5y5DboaCiUU}m- z^I#jv1kvBpf-F9so}YH9AOtAcm19l<5>kq?tt*RS;h+L)OH__-cC=i|Npv*J|1W2v zP3TQk-mn$Zp|~Q_TGaOMCjd~n?`4ieu?mH&ChnIG&vZ@3Yqp>$O3tdb+5CPmPS)Wx zSOyh$fZ_5Ybsjlgy^Tl!iX9b-9- z3N_I-sF1()t|>Kz3r_US21PXwM3-2*FNjI0p@cIt6yE`4!W+AN&RV#9>DfJt^7^{W z`>Mmiir^g_?l5H<>!SpO)p^HMi4p+ZK1U=21FB36gy?yf;l@|r>^P2F)!k;~HUbCA zJz=TO;)suP z84#&TFBRg~w1+adEP&sQ_e*NWS6V$!)>o-q5#X$(I6-5U_(w&8ZiplcH;p&kr*M~n zUZHY>8H446ozOYDH|=BN$TBnELDE$&>06{uW`r3XIIVAhLuw&C+6Hs0h91&ulFENjx!tn!HWeGQts`6t z^^06W3$J)*#ObUxpzAI8|LIEc(V1>P0GU1=qjo@}2|^F2)}3HYwfo3WrDm5bJJS&2 z^f5C};YqdtkL&DwNQ1+gp=l%c&;U#+NU0`mc>GFryi$agFdaG?3Wan#)An_Z zb`g~t2`FhVv2eW3TUxauF-s|Z3(m`w>La4Dc2Clsq_$A^H7m^>AVqJCUFi`S&qWnL zr0{?>i3=o`N$1FR{94#G}XXOb-Te0sI=a!YMnw670pB^7QQ2G5Heai8Qkn_xl_SB)SKk{bfnauEsbau z?0la}!@c>~3Biv3BQ^H>G@l6pdoe<;Y{VRGY?JE z*9f9Wi;qDiHV{K`K2J`?)hhN+Pl(-&dyGM~*k{E0t~w6X2b@be3>rqv|HKUmDhE$2 zlCRPLW@v||*SG^cC3dxK;It&c`EJ{!Aa5&-9!HdRq~01Vg>Xl;0KEszpZ8_e8FFep zOo{2yDn*0peaEfZ5lDBNTKMOH@wy3qkfeq&52Yf)bU9D6uZlwB|~s#afdF z>ZHm&R*o*QP=_8%JHmAT4if zy97>2TuhM4L}fiY>?KI-+6aUCHy=Y>P~SFSQs|36SPRS}^%ZdNtkJ(PtygwwzRj-t zsxmgGcU0eIs@Dkwso3Xz-#KO&Qg<(cf)-bXo{Ohc%l+5b?eISr%|~F3p%Z@vxVz2 zR17&es&Y1xcV=J*UeTw)-z1&zo$nJjWGH}4@5tkZ=(E!o*60U_gmPG*I;N>H4ZwS3 zYXW9s=<$Z(hZed#S)7fXtUNgtz-siIuF0gFaDKa69bG?qXO?pI@Y@moEyt-^wLK@L zWAV}D2k_CSlEE-0798O>5Wk{Om9d-NRogSY_AW({$ zgpEqm--2MtAP#>_uE)%8#PifKD06A)BnjTFlcn$`Yc%a3`bP4jHUS=bs%^5-J9-I?(XH?x%}{YF7G~cKgyL8d9_HddU^6petH>^&u87S z+<74XzV0sN{i}`QaW#T#x+4gwHG08uUw$sq z6}8#x^v;ki5$6dk%IEVm_OVDelUGOb$w^nDvM1LUatAyyHlXQNWgpiSIgl0}s5P%+ zVIMYGBo)#{8E3+(Q$cSb*CXRwmh(G?gkCADTPaF*y_M)Z zI}sVxev2d}+92_MvLEmElRQ|{R;2PsKT(#nG-BkTIS#9_#l$BGuVeYNm)TB}9J=!N z#WVEXxAN4b_?KCd1+Q|Re9pP>te2nXi6Jv#BK%s&r+5kc5UU3Hm76@bIx`ia79Ojx zatehOC&A!Eej@H;)_)_o1%y<9Ib3ctyG^Q7?y(H=Z( zB$)bEu4z<)!^nrv0G}~LaxEN{Cz)>11FV7!ej85R6ctw1LVi>yv>@7pkw)q_@)@fO z?4!24)BNC-+@YKRA3VW07ACjjfX_yg^)P^e_mk8frB=lP7M%zujzq6_GR|16gC)kC zGtrDSr#TSJ8?$yC&Gp=sIbGKEv_)4i!W-Ot)nlXgqKU37^98o!*v6-)Q;xFMV9%NS#fIEf0Y-MCD7 zSL3cxfGzZBSQ)xDBQE8q=xo+m4VUrS3#6m{&5jmjwAZr^wnS-0Va4O!@y5(U90Qu>+PYjJB#d#&z`hpv5_ z8-}ZTAFrv&L8vk4bLE*#t#3p-&tu`T+Lg87{S#1E=EFA4gsHnMP8uF>DRvl_Y3#_GemdWopq2B;yZ|{q9SXd`pJYBNQ9Q7D z^_uPYdG4WxHRRxrk7F27!D|1N-k!#<7DFz?o+{meco5|XIU5K%a?fOjavrNx@)6YE z@wYXL~f@hrJQ|)exq~ubn}yxwU7k zz5FME0XeoBIpTaKcN~p<2RuU5mOjQ|vTWIrsMoIfH}i(D(tB&CDffZfb|2pGSgz7% zvb_}VFqB#SZ{1T84It{(s9fe(7!tn8^Jci-R{+_6F;t!4>FHRI90zXUUb5( zF0`kXG1G9^3|`||`Q0KH<$}XjHMysFDpn~g8&;E|KZYY!ExwKV%e(|xj90u^mL3GY zv*gqL!~r^X?9yqc_){R|QV*$L=(DfKvMBjxFp9RyE4$ATttjm}%$maPyNp#DF*V!P zeL!2YTghj62b2h^*$1qCEWh}9I*ZzmBa43}SL7%;$NZ5(S=mOYXY z`ff-17Bk^7eMHMSx4M7JYxiAbqQC3@AU%Yy(l6NL@pX!elyBKe=BeV`J6+< z4Bc~KkH(@x$v>k1T-eAd4BcJSibXS^8v8sh+y>6mSiwWraS;8=EPa}t+tvLwN6Wf< zJiD`~3_r=~K+%!*T`~Aa>@DLi*c(TkpNGP&(-h6^3CE-yA)lhLkmKZp|j@!mm3k3EryqmEzNtM0>qBi(2_3uWGr(X}hCJ`tOiF=a}}vUi_b<9ghS z9Xm85lfu)P4)k3Xi5*kygRRs(de*GnrER14LFR;e#&@kP*qu5T-s@g5J4y8Adzqsp zS?ri@9;MRahK<@6>5<@eI8ApPG^Tm{LC}J8hk}qjv03h}qMglLT3}D?SDr<+HL)vF z=Q%lRqO4m$*fIpi!LDe{7{_Skq3zS2tIZ!H=Bl~A<}$1?Iz!%XLLbXK@tc-c#A=QB zt-G{GlAF<&(!G}UsjwCp|2** zB|Td&GlJFFVf-&`quJ!4tYqx+`5{xv>$sGww(me9=UwE=lvgn3=6#yg*8-?nv~n+D zW0o4U{Q{N7G7ZqU?E9KssR_)AT8Ys?BMavou>K*iY6j!QJ|h zTtnTlAa_(foE1BnzGK=(UTgwYJtH0S#9t$-QI9cH<6T;nmR{)}Vj11zVnuL<$9B4` ztDxN`JtBYBzjci=tQ8hq66f?!wmGx-?_ z_ukYs^o(+r<*R|!gw3_PRQ6NXWh}E%iHvjgS&}DuM&`FuT6IsiM8`07FJLimTuaa2 zwS%o)?^jt?uN_Ap=_a`_n6~17ZiBee+e{$?cq+)m)!E8>r*fGxm!ucNm|UluQA?VC z-p)>z<)F`#Md|qJH?!#G#BVGa(q)e3mZB4(-!`?LE=-20wpY>WUU0^dik`l4+YWX9 z1rFN7)eek}?;dtrRuFwx&a~`&yu0uIl}V$~C$bgoG)B>r_%&mp-{gy@53!45^iR={ zehC)x-euqYAosjUy=|&c_EPP8RD2Tg+6-^3Lymv;9%I=>i4ig1Uyi)1*GJ8uI2N;N z9^>;nPP>9y9*MuXy0`DXk#&g=i;44@bKVq9(TF&|FV`Y_;7(qxBR_Sz=6ud9JVD;i z$XT@-FHAOeSO)12FdltAyG~u7v%)NQe?&jNzouORAA_-@2b;=H+|D=Eu9ZV6lbHiF z%Ne4S%7L+OSnK|PbwWK_Xzf^^HMSGroKVjF*7%AZo1yhTTEx-oHSRX7*YdcmYWmO|z?X|uSG9==tW1c5|E6b!JM~IdGrPNZlfr~~*;@B-WzDiz(Iky{kZYmnJzNhDM8s^I_?a$EVqVw(lFNluP(oR1ax1RU> zxKk~!Ifky#=rJtk;$E*Lbq-E4@V$<@H$ts3c7DaWZ>E2ouk38PhPy!m-4%+@x_2vg z<+nv1&)GBw<|G5zuVud8=|uWAHdIP^tn{3eZ^?n;f$&OuUF5lQsT@70*7@jzS>J-b Tk!J7C^*BSjC-x#pp}FNRl8$4u diff --git a/ios/keyman/Keyman/Keyman/en.lproj/Localizable.strings b/ios/keyman/Keyman/Keyman/en.lproj/Localizable.strings index 354b5303c565bee2a0427a18167321862d68e18e..58f1783a935f5a3a3e01fb7c7a2d6c3648aab3b8 100644 GIT binary patch literal 3037 zcmcImO;02@5WVv&N~1*sD;WYuqC~LSB|-_=NJDbki>F;PZP9IaY&XN~uTNFk?ipYs zIBX7x9)G<0diBbu@8Qm-4q@d3q!wZ6{e7>)eS{v}2pgMP>tqM}B^rYUmcef#26%sZ zFe~3`x~8--Dly~?euCMRG4P>4&VD>NIQ{PSoOjvOAAaZj|6^(wRt+f@Lw`leOY+co zx3ZxJ4I^b}9>HL=0eQ>1m~_|C)}Whvy++AjxuD9f5$OOAkn#Cbq9kV#^FTF8yoeqL$$`F$h~}! z%J$K_c@iwpYi(We<;EE@lQ4pIfFTfP{D6+_Y1rVBLZi3>{xf1Eo3PxLOo%bLpvncs zMgu}}Tw(%iglPJezlq~QGDW`h`zmoLZ&kI;!O5|?oCNJiskoUM0oC@Jzcb&Yg;@pXq+oO6 zH8Uet0p!?nsrZXUE(byX_pVEu=N2IrM(zXG2p=TF6*ILu)y8m$!~we4_ z#e!-)1bnc5jNMkH?Z9e@k14lh#_UiB#fur2f5ILA{15EX?Su}UjY*VtO)2ACw%84# zz>y@9RE@rC8xVDONqR1O`+E7BS+P;4<`Y!jksxl|%D?J958a-M4J1d1j%NmmmNI_h zaz&=J_daCByycIV{)tXf^k}G@Q&(zdNWvOsvko*Lsvu3UbwymRdi4!>spyn-c3T9l z)imFPIy*#O%W(4b($%L}JWT52f~QD%RP3>Xrqr<6nsZwt++Ze zEu#I3d#KDd#cKk|WP=6&%KnNVP&)IVpq}KAn~{b9&7BEO$TR^dnbbQt2ETwqTOh>* zLE17$338_d6yw+#E)Y?=%=n_YPbLZ7pKGe;1I}|S`K(G_p$LMQFWPWed;Qu5YLe2N zKELSp=)QX$DBw4n-Vum7E~BohXb!o&CgiVmLi>N@TY%J9^B3nVJM(Gt-g#-S>zCi&RXL#9_7YjBqlCVG8MsdC~;3f=_soM!d) oLNPvIh}5reL>`bbe3|{77cSuJK1w%3RWdHyG literal 6076 zcmdUzTTdfL5QY1Bf5pfOVzbihhJ8$wDC~s@C1h8EN;aRA{Nw^G6xDI1IpNCmk=(`CQ@%>cqvv3t|!cgN^ zVX3(r%}m2ePq%UBg+8a*p0QS(YR#Gc_^kA{kd#~P9!KfV!i(@E3|iy0-rJ|uIu|*$ zG;%EmlK8Fu2VpAp=WpLsx=-boWYy0a!Dnq>{GY7qi&i^IOWB;q z>6eHP@X|yNjG|}G759Y@RS5}|P-ht0jYtGv%Qz2qZ5LQv>Uk(cUvGBJ^)?cEhhd@} zl~7^$D^I8+j?>;0k;6h}srAVwQ?4Z))N^J#i_mPMKg%)GlXGTa9yMIWXL1a`jr5J~ zYt1bsoy@_iiQYXg2jK^u)oA2cpX6qv_p^wiMqWbpLc5Su=@d>{>HoRj-1;-kFSX({ z>QzVIG|DvdKrYd;?Tw^N!)`p)^&%=0{i9#$7ktJESc7gb$53OHo)`KvoNq*p;923d1bV401Pbbt{sVTazqh918{(QI5s0!~l=a+W< z6umH!pDfBC(@+U=ZzCG85{t}}#4@*!ebaarqm|wk@{}>7VZ$Ob*S_J_N}j>MYNHkY z!Hf8Bp_S_*wthw%({(&)si=UCxwN>9+9YqB=zXR63u$&(*x(rkPOo%o6?Ve+`fO`U zRuSQ`q)=CglUeFHxsGR0XNJ;YE5xP?+R=!>;hs((i>(U zNm^H9*X4Qm@dMC>%n&Au{^8Doif%Qw>+zS%saWHg>`RbwxrXI3E_>r ziWuY516VznDsG7%{H(5?AQtqtD+Z{hZlbZB2z$*pY+8jQ?Sj9k?3K>o_(DT~;91_9C9Yi@V4dtGv}_HM~kacp6@69e#3Va=Yk|;Z@=RgRf$z zjXskix_jY^$jqFyBWb+d@8_|spuaZ%_<1S5)C6Yn0DUQ4mvtnjbB>EXz3BohR@a%P zR(Xjaqd5wXG-h{CykRpu4QMpR+3lX+E~&%9uREzhq9OH)0P~{ zyQrjfyT5uoZ>J6pM#We6dzAZzt;@AbV-N9a;*Kr~jK#XYT=-q2`~?H%?{c%>iHxPe z37%bKD0ro}eEcC=x3OS%R{FD<_`o`AYAb!T^|^MpfI~j${C-`X+vvw^UGl3l8~wG< z<(jr@v@w?5D6CF;PL0IDb`vn2$k1jnwIEHR@~J|Bs`S2 zNQ)y~Bza}oVJwZLbdN84pM>Z#o1DMtqPP#yvya6qd0cY2)Sa1_2KMm|AzA-68@2ma zY4w5RQyHivW#z>4sdMjVcjnu_9n{+8zq8?|E(1A_r(0dS7tNzw$_Kgj`(9gaviVa_ zFFkZ3Cc#I<_VGOQMl!kCVEI~4;Kef1YgZZXwign2`1)kO*AC0|Zqfb=);J!WgB1cPcGwb6g-|bB_6tLmPazhk*p=N?DrD7 nbn}7r48Mteel1qON_)!kuk`;iFrTuZl-193U1{9fT=#zfxbyg8 From 7f0a872b6b8cdc99f26978ffb62c10235e399d2c Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 4 Mar 2021 09:35:58 +0700 Subject: [PATCH 097/125] feat(ios): enables de, fr, and km localizations --- .../KeymanEngine.xcodeproj/project.pbxproj | 18 ++++++++++++++++++ .../Classes/de.lproj/ResourceInfoView.strings | 3 +++ .../Classes/fr.lproj/ResourceInfoView.strings | 3 +++ .../Keyman/Keyman.xcodeproj/project.pbxproj | 8 ++++++++ 4 files changed, 32 insertions(+) create mode 100644 ios/engine/KMEI/KeymanEngine/Classes/de.lproj/ResourceInfoView.strings create mode 100644 ios/engine/KMEI/KeymanEngine/Classes/fr.lproj/ResourceInfoView.strings diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 61f829145b..f71e371464 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -520,6 +520,14 @@ CEA9670A24BEC8030035AACF /* EngineStateBundler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EngineStateBundler.swift; sourceTree = ""; }; CEA9670C24BEEFF80035AACF /* khmer_angkor update-base.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = "khmer_angkor update-base.bundle"; sourceTree = ""; }; CEA9670E24BEF05A0035AACF /* Updates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Updates.swift; sourceTree = ""; }; + CEACC90125F07C81006EAB45 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/ResourceInfoView.strings; sourceTree = ""; }; + CEACC90325F07CAF006EAB45 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; + CEACC90425F07CB6006EAB45 /* km */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = km; path = km.lproj/Localizable.strings; sourceTree = ""; }; + CEACC90525F07CBA006EAB45 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = ""; }; + CEACC90625F07CBC006EAB45 /* km */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = km; path = km.lproj/Localizable.stringsdict; sourceTree = ""; }; + CEACC90725F07CDA006EAB45 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/ResourceInfoView.strings; sourceTree = ""; }; + CEACC90825F07CE9006EAB45 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; + CEACC90925F07CED006EAB45 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fr; path = fr.lproj/Localizable.stringsdict; sourceTree = ""; }; CEB8276624C6811800F3D39C /* KeymanHostTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeymanHostTests.swift; sourceTree = ""; }; CEC0C66B2410AC9A003E1BCD /* Sentry.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sentry.framework; path = ../../Carthage/Build/iOS/Sentry.framework; sourceTree = ""; }; CEC0C6762410E049003E1BCD /* SentryManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryManager.swift; sourceTree = ""; }; @@ -1293,6 +1301,8 @@ en, Base, km, + de, + fr, ); mainGroup = F243887314BBD43000A3E055; productRefGroup = F243887F14BBD43000A3E055 /* Products */; @@ -1672,6 +1682,9 @@ isa = PBXVariantGroup; children = ( CE96E42C24D1229A005B8E5A /* en */, + CEACC90525F07CBA006EAB45 /* de */, + CEACC90625F07CBC006EAB45 /* km */, + CEACC90925F07CED006EAB45 /* fr */, ); name = Localizable.stringsdict; sourceTree = ""; @@ -1680,6 +1693,9 @@ isa = PBXVariantGroup; children = ( CEA1486E2407808F00C6ECD2 /* en */, + CEACC90325F07CAF006EAB45 /* de */, + CEACC90425F07CB6006EAB45 /* km */, + CEACC90825F07CE9006EAB45 /* fr */, ); name = Localizable.strings; sourceTree = ""; @@ -1690,6 +1706,8 @@ CEA14871240780E100C6ECD2 /* Base */, CEA14874240780EF00C6ECD2 /* en */, CEA148772407869200C6ECD2 /* km */, + CEACC90125F07C81006EAB45 /* de */, + CEACC90725F07CDA006EAB45 /* fr */, ); name = ResourceInfoView.xib; sourceTree = ""; diff --git a/ios/engine/KMEI/KeymanEngine/Classes/de.lproj/ResourceInfoView.strings b/ios/engine/KMEI/KeymanEngine/Classes/de.lproj/ResourceInfoView.strings new file mode 100644 index 0000000000..aa12fd1fb3 --- /dev/null +++ b/ios/engine/KMEI/KeymanEngine/Classes/de.lproj/ResourceInfoView.strings @@ -0,0 +1,3 @@ + +/* Class = "UILabel"; text = "Scan this code to load this keyboard on another device"; ObjectID = "z2O-MT-IoV"; */ +"z2O-MT-IoV.text" = "Scan this code to load this keyboard on another device"; diff --git a/ios/engine/KMEI/KeymanEngine/Classes/fr.lproj/ResourceInfoView.strings b/ios/engine/KMEI/KeymanEngine/Classes/fr.lproj/ResourceInfoView.strings new file mode 100644 index 0000000000..aa12fd1fb3 --- /dev/null +++ b/ios/engine/KMEI/KeymanEngine/Classes/fr.lproj/ResourceInfoView.strings @@ -0,0 +1,3 @@ + +/* Class = "UILabel"; text = "Scan this code to load this keyboard on another device"; ObjectID = "z2O-MT-IoV"; */ +"z2O-MT-IoV.text" = "Scan this code to load this keyboard on another device"; diff --git a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj index bf008e8cb0..920c51de55 100644 --- a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj +++ b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj @@ -325,6 +325,9 @@ CE7C1AE1236925D800100C2C /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; CE7FF1EF239A0293007859D9 /* PackageBrowserViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PackageBrowserViewController.swift; sourceTree = ""; }; CE80AD32257F2B4A008D2150 /* KeymanEngine.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KeymanEngine.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CEACC90E25F07D58006EAB45 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; + CEACC90F25F07D5A006EAB45 /* km */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = km; path = km.lproj/Localizable.strings; sourceTree = ""; }; + CEACC91225F07D77006EAB45 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; CEF4E55523E95B7B0065B9C7 /* ImageBanner.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ImageBanner.xib; sourceTree = ""; }; CEF4E55823E967140065B9C7 /* ImageBannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBannerViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -772,6 +775,8 @@ en, Base, km, + fr, + de, ); mainGroup = 98ABADA7176935E400B62590; productRefGroup = 98ABADB1176935E400B62590 /* Products */; @@ -1090,6 +1095,9 @@ isa = PBXVariantGroup; children = ( CE4C654524D0020900070856 /* en */, + CEACC90E25F07D58006EAB45 /* fr */, + CEACC90F25F07D5A006EAB45 /* km */, + CEACC91225F07D77006EAB45 /* de */, ); name = Localizable.strings; sourceTree = ""; From e9e4e7bcb64f46b79247c070ccc4f5b610af6400 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 4 Mar 2021 13:41:04 +1100 Subject: [PATCH 098/125] feat(developer): &CasedKeys system store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #2241. The `&CasedKeys` system store is a compiler feature that reduces the repetitive nature of keyboard rules for `CAPS` and `NCAPS`. The `&CasedKeys` system store defines a list of virtual keys for which 'normal' Caps Lock rules apply. This store has no default value, for backward compatibility. Once this store is defined, then you can define just the unshifted and shifted versions of a rule, and Keyman Developer will synthesize the `CAPS` and `NCAPS` versions of the rule. For example, you may have the following rules: ``` store(&CasedKeys) [K_A] + [K_A] > 'α' + [SHIFT K_A] > 'Α' ``` These would be replaced by the compiler with: ``` store(&CasedKeys) [K_A] + [NCAPS K_A] > 'α' + [SHIFT CAPS K_A] > 'α' + [CAPS K_A] > 'Α' + [SHIFT NCAPS K_A] > 'Α' ``` You can also use this functionality with characters in the key part of the rule: ``` store(&CasedKeys) 'a'..'c' + 'a' > 'α' + 'A' > 'Α' ``` and the compiled expansion would be similar: ``` store(&CasedKeys) [K_A] [K_B] [K_C] + [NCAPS K_A] > 'α' + [SHIFT CAPS K_A] > 'α' + [CAPS K_A] > 'Α' + [SHIFT NCAPS K_A] > 'Α' ``` This feature is backwardly compatible with Keyman 6.0, as it is entirely implemented in the compiler. This feature is not compatible with mnemonic layouts, and the keys defined in the `&CasedKeys` store must be the unshifted base keys as found on a US English keyboard, or you can use ISO9995 identifiers if you prefer. If you define a rule where you specify either `NCAPS` or `CAPS`, for a key found in the store, then no change will be made to that rule. You can also continue to define rules which use `NCAPS` or `CAPS` for keys not found in the store. As a side-benefit, this allows the visual designer to be used and support Caps Lock, although at this stage, the `&CasedKeys` store is not surfaced in the visual designer. --- windows/src/developer/TIKE/help/kwhelp.pas | 3 +- windows/src/developer/kmcmpdll/CasedKeys.cpp | 163 +++++++++++++++ windows/src/developer/kmcmpdll/CasedKeys.h | 4 + .../kmcmpdll/CharToKeyConversion.cpp | 187 ++++++++++++++++++ .../developer/kmcmpdll/CharToKeyConversion.h | 20 ++ windows/src/developer/kmcmpdll/Compiler.cpp | 96 ++------- windows/src/developer/kmcmpdll/Compiler.rc | 9 +- .../src/developer/kmcmpdll/kmcmpdll.vcxproj | 4 + .../kmcmpdll/kmcmpdll.vcxproj.filters | 8 + windows/src/developer/kmdecomp/kmdecomp.cpp | 10 +- .../src/developer/kmdecomp/savekeyboard.cpp | 3 +- .../delphi/general/CompileErrorCodes.pas | 4 + windows/src/global/inc/Comperr.h | 4 + windows/src/global/inc/Compiler.h | 6 +- windows/src/test/unit-tests/kmcomp/test.bat | 8 + .../test/unit-tests/kmcomp/test_casedkeys.kmn | 39 ++++ .../kmcomp/test_casedkeys_chars.kmn | 41 ++++ .../kmcomp/test_casedkeys_invalid_1.kmn | 10 + .../kmcomp/test_casedkeys_invalid_2.kmn | 10 + .../kmcomp/test_casedkeys_mnemonic_1.kmn | 11 ++ .../kmcomp/test_casedkeys_mnemonic_2.kmn | 11 ++ windows/src/test/unit-tests/kmcomp/tests.kpj | 60 ++++++ 22 files changed, 626 insertions(+), 85 deletions(-) create mode 100644 windows/src/developer/kmcmpdll/CasedKeys.cpp create mode 100644 windows/src/developer/kmcmpdll/CasedKeys.h create mode 100644 windows/src/developer/kmcmpdll/CharToKeyConversion.cpp create mode 100644 windows/src/developer/kmcmpdll/CharToKeyConversion.h create mode 100644 windows/src/test/unit-tests/kmcomp/test_casedkeys.kmn create mode 100644 windows/src/test/unit-tests/kmcomp/test_casedkeys_chars.kmn create mode 100644 windows/src/test/unit-tests/kmcomp/test_casedkeys_invalid_1.kmn create mode 100644 windows/src/test/unit-tests/kmcomp/test_casedkeys_invalid_2.kmn create mode 100644 windows/src/test/unit-tests/kmcomp/test_casedkeys_mnemonic_1.kmn create mode 100644 windows/src/test/unit-tests/kmcomp/test_casedkeys_mnemonic_2.kmn diff --git a/windows/src/developer/TIKE/help/kwhelp.pas b/windows/src/developer/TIKE/help/kwhelp.pas index 35837b1095..5e9684fb27 100644 --- a/windows/src/developer/TIKE/help/kwhelp.pas +++ b/windows/src/developer/TIKE/help/kwhelp.pas @@ -41,7 +41,7 @@ type end; const - HelpArray: array[0..73] of THelpArray = ( // I4840 + HelpArray: array[0..74] of THelpArray = ( // I4840 (Word: 'BITMAP' ), (Word: 'begin' ), (Word: 'COPYRIGHT' ), @@ -53,6 +53,7 @@ const {9} (Word: 'VERSION' ), (Word: '&BITMAP'; Topic: 'bitmap' ), + (Word: '&CasedKeys'; Topic: 'casedkeys' ), (Word: '©RIGHT'; Topic: 'copyright' ), (Word: '&EthnologueCode'; Topic: 'ethnologuecode' ), // I4840 (Word: '&HOTKEY'; Topic: 'hotkey' ), diff --git a/windows/src/developer/kmcmpdll/CasedKeys.cpp b/windows/src/developer/kmcmpdll/CasedKeys.cpp new file mode 100644 index 0000000000..44473a5246 --- /dev/null +++ b/windows/src/developer/kmcmpdll/CasedKeys.cpp @@ -0,0 +1,163 @@ + +#include "pch.h" + +#include +#include +#include +#include +#include + +#include "CharToKeyConversion.h" + +extern BOOL FMnemonicLayout; // TODO: these globals should be consolidated one day + +DWORD ExpandCapsRule(PFILE_GROUP gp, PFILE_KEY kpp, PFILE_STORE sp); + +PFILE_STORE FindSystemStore(PFILE_KEYBOARD fk, DWORD dwSystemID) { + assert(fk != NULL); + assert(dwSystemID != 0); + + PFILE_STORE sp = fk->dpStoreArray; + for (DWORD i = 0; i < fk->cxStoreArray; i++, sp++) { + if (sp->dwSystemID == dwSystemID) { + return sp; + } + } + return NULL; +} + +DWORD VerifyCasedKeys(PFILE_STORE sp) { + assert(sp != NULL); + + if (FMnemonicLayout) { + // The &CasedKeys system store is not supported for + // mnemonic layouts in 14.0 + return CERR_CasedKeysNotSupportedWithMnemonicLayout; + } + + // We will rewrite this store with virtual keys + + PWSTR p = sp->dpString; + PWSTR buf = new WCHAR[wcslen(p) * 5 + 1]; // extended keys are 5 units long, so this is the max length + PWSTR q = buf; + + while (*p) { + UINT key = 0, shift = 0; + if (*p != UC_SENTINEL) { + if (!MapUSCharToVK(*p, &key, &shift)) { + return CERR_CasedKeysMustContainOnlyVirtualKeys; + } + if (shift & K_SHIFTFLAG) { + return CERR_CasedKeysMustNotIncludeShiftStates; + } + } + else { + if (*(p + 1) != CODE_EXTENDED) { + return CERR_CasedKeysMustContainOnlyVirtualKeys; + } + shift = *(p + 2); + key = *(p + 3); + if (shift != ISVIRTUALKEY) { + return CERR_CasedKeysMustNotIncludeShiftStates; + } + } + *q++ = UC_SENTINEL; + *q++ = CODE_EXTENDED; + *q++ = shift; + *q++ = key; + *q++ = UC_SENTINEL_EXTENDEDEND; + *q = 0; + + p = incxstr(p); + } + + delete[] sp->dpString; + sp->dpString = buf; + + return CERR_None; +} + +DWORD ExpandCapsRulesForGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) { + assert(fk != NULL); + assert(gp != NULL); + + if (FMnemonicLayout) { + // The &CasedKeys system store is not supported for + // mnemonic layouts in 14.0 + return CERR_None; + } + + PFILE_STORE sp = FindSystemStore(fk, TSS_CASEDKEYS); + if (!sp) { + // If there is no &CasedKeys system store, then we do not + // process the key + return CERR_None; + } + + DWORD msg; + // ExpandCapsRule may add extra rules at the end of gp->dpKeyArray, + // reallocating it, so we (a) cache the original length, and (b) + // dereference the array every call + int cxKeyArray = gp->cxKeyArray; + for (int i = 0; i < cxKeyArray; i++) { + if ((msg = ExpandCapsRule(gp, &gp->dpKeyArray[i], sp)) != CERR_None) { + return msg; + } + } + return CERR_None; +} + +DWORD ExpandCapsRule(PFILE_GROUP gp, PFILE_KEY kpp, PFILE_STORE sp) { + UINT key = kpp->Key; + UINT shift = kpp->ShiftFlags; + + if (shift == 0) { + // Convert US key cap to a virtual key + if (!MapUSCharToVK(kpp->Key, &key, &shift)) { + return CERR_None; + } + } + + if (shift & (CAPITALFLAG | NOTCAPITALFLAG)) { + // Don't attempt expansion if either Caps Lock flag is specified in the key rule + return CERR_None; + } + + PWSTR p = sp->dpString; + for (; *p; p = incxstr(p)) { + // We've already verified that the store contains only virtual keys in VerifyCasedKeys + if (*(p + 3) == key) { + break; + } + } + + if (!*p) { + // This key is not modified by Caps Lock + return CERR_None; + } + + // This key is modified by Caps Lock, so we need to duplicate this rule + PFILE_KEY k = new FILE_KEY[gp->cxKeyArray + 1]; + if (!k) return CERR_CannotAllocateMemory; + memcpy(k, gp->dpKeyArray, gp->cxKeyArray * sizeof(FILE_KEY)); + + kpp = &k[(INT_PTR)(kpp - gp->dpKeyArray)]; + + delete gp->dpKeyArray; + gp->dpKeyArray = k; + gp->cxKeyArray++; + + k = &k[gp->cxKeyArray - 1]; + k->dpContext = new WCHAR[wcslen(kpp->dpContext) + 1]; + k->dpOutput = new WCHAR[wcslen(kpp->dpOutput) + 1]; + wcscpy_s(k->dpContext, wcslen(kpp->dpContext) + 1, kpp->dpContext); // copy the context. + wcscpy_s(k->dpOutput, wcslen(kpp->dpOutput) + 1, kpp->dpOutput); // copy the output. + k->Key = key; + k->Line = kpp->Line; + // Add the CAPITAL FLAG, invert shift flag for the rule + k->ShiftFlags = shift ^ K_SHIFTFLAG | CAPITALFLAG; + kpp->Key = key; + kpp->ShiftFlags = shift | NOTCAPITALFLAG; + + return CERR_None; +} diff --git a/windows/src/developer/kmcmpdll/CasedKeys.h b/windows/src/developer/kmcmpdll/CasedKeys.h new file mode 100644 index 0000000000..66ba8a312d --- /dev/null +++ b/windows/src/developer/kmcmpdll/CasedKeys.h @@ -0,0 +1,4 @@ +#pragma once + +DWORD VerifyCasedKeys(PFILE_STORE sp); +DWORD ExpandCapsRulesForGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp); diff --git a/windows/src/developer/kmcmpdll/CharToKeyConversion.cpp b/windows/src/developer/kmcmpdll/CharToKeyConversion.cpp new file mode 100644 index 0000000000..24723c980f --- /dev/null +++ b/windows/src/developer/kmcmpdll/CharToKeyConversion.cpp @@ -0,0 +1,187 @@ + +#include "pch.h" +#include + +/* Following code lifted from syskbd.cpp and tweaked for compiler use. Todo: consolidate */ + +WCHAR VKToChar(WORD keyCode, UINT shiftFlags) +{ + char shiftedDigit[] = ")!@#$%^&*("; + int n, Shift; + + if (!(shiftFlags & ISVIRTUALKEY)) return keyCode; + + if (shiftFlags & (LCTRLFLAG | RCTRLFLAG | LALTFLAG | RALTFLAG)) return 0; + + if (keyCode >= '0' && keyCode <= '9') + { + n = keyCode - '0'; + return ((shiftFlags & K_SHIFTFLAG) ? shiftedDigit[n] : keyCode); + } + + if (keyCode >= 'A' && keyCode <= 'Z') + { + Shift = (shiftFlags & K_SHIFTFLAG); + if (shiftFlags & (CAPITALFLAG)) Shift = !Shift; + return (Shift ? keyCode : keyCode + 32); + } + + if (keyCode >= VK_NUMPAD0 && keyCode <= VK_NUMPAD9) + { + if (!(shiftFlags & NUMLOCKFLAG)) return 0; + return keyCode - (VK_NUMPAD0 - '0'); + } + + Shift = (shiftFlags & K_SHIFTFLAG); + + switch (keyCode) + { + case VK_ACCENT: + return Shift ? '~' : '`'; + case VK_HYPHEN: + return Shift ? '_' : '-'; + case VK_EQUAL: + return Shift ? '+' : '='; + case VK_BKSLASH: + return Shift ? '|' : 92; + case VK_LBRKT: + return Shift ? '{' : '['; + case VK_RBRKT: + return Shift ? '}' : ']'; + case VK_COLON: + return Shift ? ':' : ';'; + case VK_QUOTE: + return Shift ? '"' : 39; + case VK_COMMA: + return Shift ? '<' : ','; + case VK_PERIOD: + return Shift ? '>' : '.'; + case VK_SLASH: + return Shift ? '?' : '/'; + case VK_SPACE: + return ' '; + } + return 0; + //keyCode; +} + + +/* This array is lifted from preservedkeymap.cpp */ +// TODO: share this +const struct +{ + UINT key; + BOOL shift; +} USCharMap[] = { + { VK_SPACE, FALSE }, // 20 ' ' + { '1', TRUE }, // 21 '!' + { VK_QUOTE, TRUE }, // 22 '"' + { '3', TRUE }, // 23 '#' + { '4', TRUE }, // 24 '$' + { '5', TRUE }, // 25 '%' + { '7', TRUE }, // 26 '&' + { VK_QUOTE, FALSE }, // 27 ''' + { '9', TRUE }, // 28 '(' + { '0', TRUE }, // 29 ')' + { '8', TRUE }, // 2A '*' + { VK_EQUAL, TRUE }, // 2B '+' + { VK_COMMA, FALSE }, // 2C ',' + { VK_HYPHEN, FALSE }, // 2D '-' + { VK_PERIOD, FALSE }, // 2E '.' + { VK_SLASH, FALSE }, // 2F '/' + + { '0', FALSE }, // 30 '0' + { '1', FALSE }, // 31 '1' + { '2', FALSE }, // 32 '2' + { '3', FALSE }, // 33 '3' + { '4', FALSE }, // 34 '4' + { '5', FALSE }, // 35 '5' + { '6', FALSE }, // 36 '6' + { '7', FALSE }, // 37 '7' + { '8', FALSE }, // 38 '8' + { '9', FALSE }, // 39 '9' + { VK_COLON, TRUE }, // 3A ':' + { VK_COLON, FALSE }, // 3B ';' + { VK_COMMA, TRUE }, // 3C '<' + { VK_EQUAL, FALSE }, // 3D '=' + { VK_PERIOD, TRUE }, // 3E '>' + { VK_SLASH, TRUE }, // 3F '?' + + { '2', TRUE }, // 40 '@' + { 'A', TRUE }, // 41 'A' + { 'B', TRUE }, // 42 'B' + { 'C', TRUE }, // 43 'C' + { 'D', TRUE }, // 44 'D' + { 'E', TRUE }, // 45 'E' + { 'F', TRUE }, // 46 'F' + { 'G', TRUE }, // 47 'G' + { 'H', TRUE }, // 48 'H' + { 'I', TRUE }, // 49 'I' + { 'J', TRUE }, // 4A 'J' + { 'K', TRUE }, // 4B 'K' + { 'L', TRUE }, // 4C 'L' + { 'M', TRUE }, // 4D 'M' + { 'N', TRUE }, // 4E 'N' + { 'O', TRUE }, // 4F 'O' + + { 'P', TRUE }, // 50 'P' + { 'Q', TRUE }, // 51 'Q' + { 'R', TRUE }, // 52 'R' + { 'S', TRUE }, // 53 'S' + { 'T', TRUE }, // 54 'T' + { 'U', TRUE }, // 55 'U' + { 'V', TRUE }, // 56 'V' + { 'W', TRUE }, // 57 'W' + { 'X', TRUE }, // 58 'X' + { 'Y', TRUE }, // 59 'Y' + { 'Z', TRUE }, // 5A 'Z' + { VK_LBRKT, FALSE }, // 5B '[' + { VK_BKSLASH, FALSE }, // 5C '\' + { VK_RBRKT, FALSE }, // 5D ']' + { '6', TRUE }, // 5E '^' + { VK_HYPHEN, TRUE }, // 5F '_' + + { VK_ACCENT, FALSE }, // 60 '`' + { 'A', FALSE }, // 61 'a' + { 'B', FALSE }, // 62 'b' + { 'C', FALSE }, // 63 'c' + { 'D', FALSE }, // 64 'd' + { 'E', FALSE }, // 65 'e' + { 'F', FALSE }, // 66 'f' + { 'G', FALSE }, // 67 'g' + { 'H', FALSE }, // 68 'h' + { 'I', FALSE }, // 69 'i' + { 'J', FALSE }, // 6A 'j' + { 'K', FALSE }, // 6B 'k' + { 'L', FALSE }, // 6C 'l' + { 'M', FALSE }, // 6D 'm' + { 'N', FALSE }, // 6E 'n' + { 'O', FALSE }, // 6F 'o' + + { 'P', FALSE }, // 70 'p' + { 'Q', FALSE }, // 71 'q' + { 'R', FALSE }, // 72 'r' + { 'S', FALSE }, // 73 's' + { 'T', FALSE }, // 74 't' + { 'U', FALSE }, // 75 'u' + { 'V', FALSE }, // 76 'v' + { 'W', FALSE }, // 77 'w' + { 'X', FALSE }, // 78 'x' + { 'Y', FALSE }, // 79 'y' + { 'Z', FALSE }, // 7A 'z' + { VK_LBRKT, TRUE }, // 7B '{' + { VK_BKSLASH, TRUE }, // 7C '|' + { VK_RBRKT, TRUE }, // 7D '}' + { VK_ACCENT, TRUE } // 7E '~' +}; + +BOOL MapUSCharToVK(UINT ch, UINT *puKey, UINT *puShiftFlags) { + assert(puKey != NULL); + assert(puShiftFlags != NULL); + if (ch >= 0x20 && ch < 0x7F) { + *puKey = USCharMap[ch - 0x20].key; + *puShiftFlags = ISVIRTUALKEY | (USCharMap[ch - 0x20].shift ? K_SHIFTFLAG : 0); + return TRUE; + } + return FALSE; +} diff --git a/windows/src/developer/kmcmpdll/CharToKeyConversion.h b/windows/src/developer/kmcmpdll/CharToKeyConversion.h new file mode 100644 index 0000000000..6524a73f99 --- /dev/null +++ b/windows/src/developer/kmcmpdll/CharToKeyConversion.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#define VK_COLON 0xBA +#define VK_EQUAL 0xBB +#define VK_COMMA 0xBC +#define VK_HYPHEN 0xBD +#define VK_PERIOD 0xBE +#define VK_SLASH 0xBF +#define VK_ACCENT 0xC0 +#define VK_LBRKT 0xDB +#define VK_BKSLASH 0xDC +#define VK_RBRKT 0xDD +#define VK_QUOTE 0xDE +#define VK_xDF 0xDF + +BOOL MapUSCharToVK(UINT ch, UINT *puKey, UINT *puShiftFlags); +WCHAR VKToChar(WORD keyCode, UINT shiftFlags); + diff --git a/windows/src/developer/kmcmpdll/Compiler.cpp b/windows/src/developer/kmcmpdll/Compiler.cpp index c4c79216a8..147224a0aa 100644 --- a/windows/src/developer/kmcmpdll/Compiler.cpp +++ b/windows/src/developer/kmcmpdll/Compiler.cpp @@ -85,6 +85,8 @@ #include "edition.h" #include "onlineconstants.h" +#include "CharToKeyConversion.h" +#include "CasedKeys.h" int xatoi(PWSTR *p); int atoiW(PWSTR p); @@ -108,7 +110,7 @@ DWORD ProcessGroupFinish(PFILE_KEYBOARD fk); DWORD ProcessGroupLine(PFILE_KEYBOARD fk, PWSTR p); DWORD ProcessStoreLine(PFILE_KEYBOARD fk, PWSTR p); DWORD AddDebugStore(PFILE_KEYBOARD fk, PWSTR str); -DWORD ProcessKeyLine(PFILE_KEYBOARD fk, PWSTR str, int IsUnicode); +DWORD ProcessKeyLine(PFILE_KEYBOARD fk, PWSTR str, BOOL IsUnicode); DWORD ProcessEthnologueStore(PWSTR p); // I2646 DWORD ProcessHotKey(PWSTR p, DWORD *hk); DWORD ImportBitmapFile(PFILE_KEYBOARD fk, PWSTR szName, PDWORD FileSize, PBYTE *Buf); @@ -190,6 +192,7 @@ const PWCHAR StoreTokens[TSS__MAX + 2] = { SSN__PREFIX L"KEYBOARDVERSION", // I4140 SSN__PREFIX L"KMW_EMBEDCSS", SSN__PREFIX L"TARGETS", // I4504 + SSN__PREFIX L"CASEDKEYS", // #2241 NULL }; @@ -869,83 +872,6 @@ DWORD ProcessGroupLine(PFILE_KEYBOARD fk, PWSTR p) return CERR_None; } -/* Following code lifted from syskbd.cpp and tweaked for compiler use. Todo: consolidate */ - -#define VK_COLON 0xBA -#define VK_EQUAL 0xBB -#define VK_COMMA 0xBC -#define VK_HYPHEN 0xBD -#define VK_PERIOD 0xBE -#define VK_SLASH 0xBF -#define VK_ACCENT 0xC0 -#define VK_LBRKT 0xDB -#define VK_BKSLASH 0xDC -#define VK_RBRKT 0xDD -#define VK_QUOTE 0xDE -#define VK_xDF 0xDF - -WCHAR VKToChar(WORD keyCode, UINT shiftFlags) -{ - char shiftedDigit[] = ")!@#$%^&*("; - int n, Shift; - - if (!(shiftFlags & ISVIRTUALKEY)) return keyCode; - - if (shiftFlags & (LCTRLFLAG | RCTRLFLAG | LALTFLAG | RALTFLAG)) return 0; - - if (keyCode >= '0' && keyCode <= '9') - { - n = keyCode - '0'; - return ((shiftFlags & K_SHIFTFLAG) ? shiftedDigit[n] : keyCode); - } - - if (keyCode >= 'A' && keyCode <= 'Z') - { - Shift = (shiftFlags & K_SHIFTFLAG); - if (shiftFlags & (CAPITALFLAG)) Shift = !Shift; - return (Shift ? keyCode : keyCode + 32); - } - - if (keyCode >= VK_NUMPAD0 && keyCode <= VK_NUMPAD9) - { - if (!(shiftFlags & NUMLOCKFLAG)) return 0; - return keyCode - (VK_NUMPAD0 - '0'); - } - - Shift = (shiftFlags & K_SHIFTFLAG); - - switch (keyCode) - { - case VK_ACCENT: - return Shift ? '~' : '`'; - case VK_HYPHEN: - return Shift ? '_' : '-'; - case VK_EQUAL: - return Shift ? '+' : '='; - case VK_BKSLASH: - return Shift ? '|' : 92; - case VK_LBRKT: - return Shift ? '{' : '['; - case VK_RBRKT: - return Shift ? '}' : ']'; - case VK_COLON: - return Shift ? ':' : ';'; - case VK_QUOTE: - return Shift ? '"' : 39; - case VK_COMMA: - return Shift ? '<' : ','; - case VK_PERIOD: - return Shift ? '>' : '.'; - case VK_SLASH: - return Shift ? '?' : '/'; - case VK_SPACE: - return ' '; - } - return 0; - //keyCode; -} - - int cmpkeys(const void *key, const void *elem) { PFILE_KEY akey, aelem; @@ -973,6 +899,7 @@ int cmpkeys(const void *key, const void *elem) DWORD ProcessGroupFinish(PFILE_KEYBOARD fk) { PFILE_GROUP gp; + DWORD msg; if (fk->currentGroup == 0xFFFFFFFF) return CERR_None; // Just got to first group - so nothing to finish yet @@ -980,6 +907,7 @@ DWORD ProcessGroupFinish(PFILE_KEYBOARD fk) gp = &fk->dpGroupArray[fk->currentGroup]; // Finish off the previous group stuff! + if ((msg = ExpandCapsRulesForGroup(fk, gp)) != CERR_None) return msg; qsort(gp->dpKeyArray, gp->cxKeyArray, sizeof(FILE_KEY), cmpkeys); return CERR_None; @@ -1254,6 +1182,11 @@ DWORD ProcessSystemStore(PFILE_KEYBOARD fk, DWORD SystemID, PFILE_STORE sp) case TSS_MNEMONIC: VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_MnemonicLayout); FMnemonicLayout = atoiW(sp->dpString) == 1; + if (FMnemonicLayout) { + // The &CasedKeys system store is not supported for + // mnemonic layouts + return CERR_CasedKeysNotSupportedWithMnemonicLayout; + } break; case TSS_NAME: @@ -1384,6 +1317,13 @@ DWORD ProcessSystemStore(PFILE_KEYBOARD fk, DWORD SystemID, PFILE_STORE sp) } break; + + case TSS_CASEDKEYS: + if ((msg = VerifyCasedKeys(sp)) != CERR_None) { + return msg; + } + break; + default: return CERR_InvalidSystemStore; } diff --git a/windows/src/developer/kmcmpdll/Compiler.rc b/windows/src/developer/kmcmpdll/Compiler.rc index f072c02d7a..1cb5a36b57 100644 --- a/windows/src/developer/kmcmpdll/Compiler.rc +++ b/windows/src/developer/kmcmpdll/Compiler.rc @@ -236,4 +236,11 @@ BEGIN CERR_CharacterExpansionMustBeFollowedByCharacter "A character expansion must be terminated by a character key" CERR_VKeyExpansionMustUseConsistentShift "A virtual key expansion must use the same shift state for both terminators" CERR_ExpansionMustBePositive "An expansion must have positive difference (i.e. A-Z, not Z-A)" -END \ No newline at end of file +END + +STRINGTABLE +BEGIN + CERR_CasedKeysMustContainOnlyVirtualKeys "The &CasedKeys system store must contain only virtual keys or characters found on a US English keyboard" + CERR_CasedKeysMustNotIncludeShiftStates "The &CasedKeys system store must not include shift states" + CERR_CasedKeysNotSupportedWithMnemonicLayout "The &CasedKeys system store is not supported with mnemonic layouts" +END diff --git a/windows/src/developer/kmcmpdll/kmcmpdll.vcxproj b/windows/src/developer/kmcmpdll/kmcmpdll.vcxproj index 7866ee077f..6556493822 100644 --- a/windows/src/developer/kmcmpdll/kmcmpdll.vcxproj +++ b/windows/src/developer/kmcmpdll/kmcmpdll.vcxproj @@ -294,6 +294,8 @@ + + @@ -330,6 +332,8 @@ NotUsing + + %(AdditionalIncludeDirectories) %(AdditionalIncludeDirectories) diff --git a/windows/src/developer/kmcmpdll/kmcmpdll.vcxproj.filters b/windows/src/developer/kmcmpdll/kmcmpdll.vcxproj.filters index 4d1a0d7f75..b20b6a6219 100644 --- a/windows/src/developer/kmcmpdll/kmcmpdll.vcxproj.filters +++ b/windows/src/developer/kmcmpdll/kmcmpdll.vcxproj.filters @@ -70,6 +70,12 @@ Header files + + Header files + + + Header files + @@ -89,6 +95,8 @@ + + diff --git a/windows/src/developer/kmdecomp/kmdecomp.cpp b/windows/src/developer/kmdecomp/kmdecomp.cpp index b43a3c50d8..7fa28ed79d 100644 --- a/windows/src/developer/kmdecomp/kmdecomp.cpp +++ b/windows/src/developer/kmdecomp/kmdecomp.cpp @@ -59,14 +59,20 @@ int run(int argc, char *argv[]) { puts("KMDECOMP: Decompile Keyman .kmx keyboard"); puts("(C) SIL International"); - puts("Usage: KMDECOMP \n"); + puts("Usage: KMDECOMP [output]\n"); puts("Will create a .kmn and optionally a .bmp/.ico with the same filename and in the same location as the input"); return 1; } if(!LoadKeyboard(argv[1], &kbd, &lpBitmap, &cbBitmap)) return 2; - _splitpath_s(argv[1], drive, _MAX_DRIVE, dir, _MAX_DIR, filename, _MAX_FNAME, NULL, 0); + if (argc < 3) { + _splitpath_s(argv[1], drive, _MAX_DRIVE, dir, _MAX_DIR, filename, _MAX_FNAME, NULL, 0); + } + else { + _splitpath_s(argv[2], drive, _MAX_DRIVE, dir, _MAX_DIR, filename, _MAX_FNAME, NULL, 0); + } + _makepath_s(buf, _MAX_PATH, drive, dir, filename, ".kmn"); _makepath_s(buf2, _MAX_PATH, drive, dir, filename, ".bmp"); diff --git a/windows/src/developer/kmdecomp/savekeyboard.cpp b/windows/src/developer/kmdecomp/savekeyboard.cpp index 74bc135147..0d323f3933 100644 --- a/windows/src/developer/kmdecomp/savekeyboard.cpp +++ b/windows/src/developer/kmdecomp/savekeyboard.cpp @@ -90,6 +90,7 @@ const PWCHAR StoreTokens[MAX_SYSTEM_STORE] = { // I4652 SSN__PREFIX L"KEYBOARDVERSION", // I4140 SSN__PREFIX L"KMW_EMBEDCSS", SSN__PREFIX L"TARGETS", // I4504 + SSN__PREFIX L"CASEDKEYS", NULL }; @@ -254,7 +255,7 @@ PWCHAR ExtString(PWCHAR str) else wsprintfW(p, L"[%s%s] ", flagstr(*str), VKeyNames[*(str+1)]); } - str++; + str+=2; // skip UC_SENTINEL_EXTENDEDEND p = wcschr(p, 0); break; case CODE_SWITCH: diff --git a/windows/src/global/delphi/general/CompileErrorCodes.pas b/windows/src/global/delphi/general/CompileErrorCodes.pas index b1a58252f1..78d6b42abd 100644 --- a/windows/src/global/delphi/general/CompileErrorCodes.pas +++ b/windows/src/global/delphi/general/CompileErrorCodes.pas @@ -153,6 +153,10 @@ const CERR_VKeyExpansionMustUseConsistentShift = $4067; CERR_ExpansionMustBePositive = $4068; + CERR_CasedKeysMustContainOnlyVirtualKeys = $4069; + CERR_CasedKeysMustNotIncludeShiftStates = $406A; + CERR_CasedKeysNotSupportedWithMnemonicLayout = $406B; + CWARN_TooManyWarnings = $2080; CWARN_OldVersion = $2081; CWARN_BitmapNotUsed = $2082; diff --git a/windows/src/global/inc/Comperr.h b/windows/src/global/inc/Comperr.h index 4ac2f5fb8f..a8cc49ec7f 100644 --- a/windows/src/global/inc/Comperr.h +++ b/windows/src/global/inc/Comperr.h @@ -149,6 +149,10 @@ #define CERR_VKeyExpansionMustUseConsistentShift 0x00004067 #define CERR_ExpansionMustBePositive 0x00004068 +#define CERR_CasedKeysMustContainOnlyVirtualKeys 0x00004069 +#define CERR_CasedKeysMustNotIncludeShiftStates 0x0000406A +#define CERR_CasedKeysNotSupportedWithMnemonicLayout 0x0000406B + #define CWARN_TooManyWarnings 0x00002080 #define CWARN_OldVersion 0x00002081 #define CWARN_BitmapNotUsed 0x00002082 diff --git a/windows/src/global/inc/Compiler.h b/windows/src/global/inc/Compiler.h index 1123dc5389..be1a7a7a41 100644 --- a/windows/src/global/inc/Compiler.h +++ b/windows/src/global/inc/Compiler.h @@ -199,9 +199,11 @@ #define TSS_KEYBOARDVERSION 36 // &keyboardversion system store // I4140 #define TSS_KMW_EMBEDCSS 37 -#define TSS_TARGETS 38 +#define TSS_TARGETS 38 -#define TSS__MAX 38 +#define TSS_CASEDKEYS 39 + +#define TSS__MAX 39 /* wm_keyman_control_internal message control codes */ diff --git a/windows/src/test/unit-tests/kmcomp/test.bat b/windows/src/test/unit-tests/kmcomp/test.bat index 0c580bd9f0..80be2b6187 100644 --- a/windows/src/test/unit-tests/kmcomp/test.bat +++ b/windows/src/test/unit-tests/kmcomp/test.bat @@ -46,6 +46,14 @@ fc /b test_expansion.kmx test_expansion_1.kmx || goto :eof call :should-fail "#2241: invalid expansions" test_expansion_invalid.kmn || goto :eof +call :should-pass "#2241: &CasedKeys" test_casedkeys.kmn || goto :eof +call :should-pass "#2241: &CasedKeys (chars)" test_casedkeys_chars.kmn || goto :eof + +call :should-fail "#2241: &CasedKeys (mnemonic 1)" test_casedkeys_mnemonic_1.kmn || goto :eof +call :should-fail "#2241: &CasedKeys (mnemonic 2)" test_casedkeys_mnemonic_2.kmn || goto :eof +call :should-fail "#2241: &CasedKeys (invalid chars 1)" test_casedkeys_invalid_1.kmn || goto :eof +call :should-fail "#2241: &CasedKeys (invalid chars 2)" test_casedkeys_invalid_2.kmn || goto :eof + goto :eof :should-pass diff --git a/windows/src/test/unit-tests/kmcomp/test_casedkeys.kmn b/windows/src/test/unit-tests/kmcomp/test_casedkeys.kmn new file mode 100644 index 0000000000..4962ca039f --- /dev/null +++ b/windows/src/test/unit-tests/kmcomp/test_casedkeys.kmn @@ -0,0 +1,39 @@ +c Tests cased keys rule expansions +store(&NAME) 'Test &CasedKeys' +begin Unicode > use(main) + +group(main) using keys + +store(&CasedKeys) [K_A] [K_E] [K_I] [K_O] [K_U] [K_1] .. [K_4] + ++ [K_A] > 'aaah' ++ [SHIFT K_A] > 'AAAH' + ++ [K_E] > 'eh' ++ [SHIFT K_E] > 'EH' + ++ [K_O] > 'oh' ++ [SHIFT K_O] > 'OH' + ++ [K_1] > 'small one' ++ [SHIFT K_1] > 'big one' + +c These ones should not be expanded, because they are unreferenced + ++ [K_5] > 'small five' ++ [SHIFT K_5] > 'big five' + ++ [K_C] > 'small c' ++ [SHIFT K_C] > 'big c' + +c These should not be expanded either, because they are capsified already + ++ [CAPS K_B] > 'Caps B' ++ [NCAPS K_B] > 'NCaps B' ++ [CAPS SHIFT K_B] > 'Caps Shift B' ++ [NCAPS SHIFT K_B] > 'NCaps Shift B' + ++ [CAPS K_I] > 'Caps I' ++ [NCAPS K_I] > 'NCaps I' ++ [CAPS SHIFT K_I] > 'Caps Shift I' ++ [NCAPS SHIFT K_I] > 'NCaps Shift I' diff --git a/windows/src/test/unit-tests/kmcomp/test_casedkeys_chars.kmn b/windows/src/test/unit-tests/kmcomp/test_casedkeys_chars.kmn new file mode 100644 index 0000000000..c159a02a9b --- /dev/null +++ b/windows/src/test/unit-tests/kmcomp/test_casedkeys_chars.kmn @@ -0,0 +1,41 @@ +c Tests cased keys rule expansions - character-based rules +store(&NAME) 'Test &CasedKeys' +begin Unicode > use(main) + +group(main) using keys + +store(&CasedKeys) 'a'..'z1/' + ++ 'a' > 'aaah' ++ 'A' > 'AAAH' + ++ 'e' > 'eh' ++ 'E' > 'EH' + ++ 'o' > 'oh' ++ 'O' > 'OH' + ++ '1' > 'small one' ++ '!' > 'big one' + ++ '/' > 'little slash' ++ '?' > 'big slash' + +c These ones should not be expanded, because they are unreferenced + ++ '5' > 'small five' ++ '%' > 'big five' + + + +c These should not be expanded either, because they are capsified already + ++ [CAPS K_B] > 'Caps B' ++ [NCAPS K_B] > 'NCaps B' ++ [CAPS SHIFT K_B] > 'Caps Shift B' ++ [NCAPS SHIFT K_B] > 'NCaps Shift B' + ++ [CAPS K_I] > 'Caps I' ++ [NCAPS K_I] > 'NCaps I' ++ [CAPS SHIFT K_I] > 'Caps Shift I' ++ [NCAPS SHIFT K_I] > 'NCaps Shift I' diff --git a/windows/src/test/unit-tests/kmcomp/test_casedkeys_invalid_1.kmn b/windows/src/test/unit-tests/kmcomp/test_casedkeys_invalid_1.kmn new file mode 100644 index 0000000000..8bc65cced6 --- /dev/null +++ b/windows/src/test/unit-tests/kmcomp/test_casedkeys_invalid_1.kmn @@ -0,0 +1,10 @@ +c Tests cased keys - invalid codes +store(&NAME) 'Test &CasedKeys' +begin Unicode > use(main) + +group(main) using keys + +store(&CasedKeys) U+1000 + ++ [K_A] > 'aaah' ++ [SHIFT K_A] > 'AAAH' diff --git a/windows/src/test/unit-tests/kmcomp/test_casedkeys_invalid_2.kmn b/windows/src/test/unit-tests/kmcomp/test_casedkeys_invalid_2.kmn new file mode 100644 index 0000000000..508466c07e --- /dev/null +++ b/windows/src/test/unit-tests/kmcomp/test_casedkeys_invalid_2.kmn @@ -0,0 +1,10 @@ +c Tests cased keys - invalid codes +store(&NAME) 'Test &CasedKeys' +begin Unicode > use(main) + +group(main) using keys + +store(&CasedKeys) [SHIFT K_1] + ++ [K_A] > 'aaah' ++ [SHIFT K_A] > 'AAAH' diff --git a/windows/src/test/unit-tests/kmcomp/test_casedkeys_mnemonic_1.kmn b/windows/src/test/unit-tests/kmcomp/test_casedkeys_mnemonic_1.kmn new file mode 100644 index 0000000000..40b0bbb966 --- /dev/null +++ b/windows/src/test/unit-tests/kmcomp/test_casedkeys_mnemonic_1.kmn @@ -0,0 +1,11 @@ +c Tests cased keys - invalid with mnemonic layout +store(&NAME) 'Test &CasedKeys' +store(&mnemoniclayout) '1' +begin Unicode > use(main) + +group(main) using keys + +store(&CasedKeys) [K_A] [K_E] [K_I] [K_O] [K_U] [K_1] .. [K_4] + ++ [K_A] > 'aaah' ++ [SHIFT K_A] > 'AAAH' diff --git a/windows/src/test/unit-tests/kmcomp/test_casedkeys_mnemonic_2.kmn b/windows/src/test/unit-tests/kmcomp/test_casedkeys_mnemonic_2.kmn new file mode 100644 index 0000000000..d2ca8faceb --- /dev/null +++ b/windows/src/test/unit-tests/kmcomp/test_casedkeys_mnemonic_2.kmn @@ -0,0 +1,11 @@ +c Tests cased keys - invalid with mnemonic layout +store(&NAME) 'Test &CasedKeys' +begin Unicode > use(main) + +group(main) using keys + +store(&CasedKeys) [K_A] [K_E] [K_I] [K_O] [K_U] [K_1] .. [K_4] +store(&mnemoniclayout) '1' + ++ [K_A] > 'aaah' ++ [SHIFT K_A] > 'AAAH' diff --git a/windows/src/test/unit-tests/kmcomp/tests.kpj b/windows/src/test/unit-tests/kmcomp/tests.kpj index c5d7e56564..8cfa81c9d4 100644 --- a/windows/src/test/unit-tests/kmcomp/tests.kpj +++ b/windows/src/test/unit-tests/kmcomp/tests.kpj @@ -156,6 +156,66 @@ Test Invalid Expansions
+ + id_c35b5f806c85252da940a60449e089e0 + test_casedkeys.kmn + test_casedkeys.kmn + 1.0 + .kmn +
+ Test &CasedKeys +
+
+ + id_3a9d89cd15f05037ef2d5a71fd78ed35 + test_casedkeys_chars.kmn + test_casedkeys_chars.kmn + 1.0 + .kmn +
+ Test &CasedKeys +
+
+ + id_70997591a064ca387dac08622a04c392 + test_casedkeys_invalid_1.kmn + test_casedkeys_invalid_1.kmn + 1.0 + .kmn +
+ Test &CasedKeys +
+
+ + id_8551121eb12d38673567587b753908f7 + test_casedkeys_invalid_2.kmn + test_casedkeys_invalid_2.kmn + 1.0 + .kmn +
+ Test &CasedKeys +
+
+ + id_2e7200332135cd7d8ce6109a31be82fe + test_casedkeys_mnemonic_1.kmn + test_casedkeys_mnemonic_1.kmn + 1.0 + .kmn +
+ Test &CasedKeys +
+
+ + id_7d86f03f3e1c308ab03429ac05556e5a + test_casedkeys_mnemonic_2.kmn + test_casedkeys_mnemonic_2.kmn + 1.0 + .kmn +
+ Test &CasedKeys +
+
id_5f9e583f7392151b3c3a7d4577b31a4f test_valid.kmx From d20bb5d37db700bd8ab8ed1a977b3c9cb7b70247 Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 4 Mar 2021 09:50:22 +0700 Subject: [PATCH 099/125] fix(ios): ResourceInfoView.strings l10n mapping correction --- crowdin.yml | 2 +- .../Classes/LanguagePicker/de.lproj/ResourceInfoView.strings | 2 -- .../Classes/LanguagePicker/fr.lproj/ResourceInfoView.strings | 2 -- .../Classes/LanguagePicker/km.lproj/ResourceInfoView.strings | 2 -- .../KeymanEngine/Classes/de.lproj/ResourceInfoView.strings | 3 +-- .../KeymanEngine/Classes/fr.lproj/ResourceInfoView.strings | 3 +-- .../KeymanEngine/Classes/km.lproj/ResourceInfoView.strings | 3 +-- 7 files changed, 4 insertions(+), 13 deletions(-) delete mode 100644 ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/de.lproj/ResourceInfoView.strings delete mode 100644 ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/fr.lproj/ResourceInfoView.strings delete mode 100644 ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/km.lproj/ResourceInfoView.strings diff --git a/crowdin.yml b/crowdin.yml index 722e840bad..379ddeec07 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -68,7 +68,7 @@ files: - source: /ios/engine/KMEI/KeymanEngine/Classes/en.lproj/ResourceInfoView.strings dest: /ios/engine/ResourceInfoView.strings - translation: /ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/%osx_code%/%original_file_name% + translation: /ios/engine/KMEI/KeymanEngine/Classes/%osx_code%/%original_file_name% - source: /ios/engine/KMEI/KeymanEngine/en.lproj/Localizable.strings dest: /ios/engine/Localizable.strings diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/de.lproj/ResourceInfoView.strings b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/de.lproj/ResourceInfoView.strings deleted file mode 100644 index e6af056038..0000000000 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/de.lproj/ResourceInfoView.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Class = "UILabel"; text = "Scan this code to load this keyboard on another device"; ObjectID = "z2O-MT-IoV"; */ -"z2O-MT-IoV.text" = "Scannen Sie diesen Code, um diese Tastatur auf einem anderen Gerät zu laden"; diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/fr.lproj/ResourceInfoView.strings b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/fr.lproj/ResourceInfoView.strings deleted file mode 100644 index 5b0e4ea151..0000000000 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/fr.lproj/ResourceInfoView.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Class = "UILabel"; text = "Scan this code to load this keyboard on another device"; ObjectID = "z2O-MT-IoV"; */ -"z2O-MT-IoV.text" = "Scannez ce code pour charger ce clavier sur un autre appareil"; diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/km.lproj/ResourceInfoView.strings b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/km.lproj/ResourceInfoView.strings deleted file mode 100644 index 10e17b7cc9..0000000000 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/km.lproj/ResourceInfoView.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Class = "UILabel"; text = "Scan this code to load this keyboard on another device"; ObjectID = "z2O-MT-IoV"; */ -"z2O-MT-IoV.text" = "ស្កេនកូដ​នេះដើម្បី​ផ្ទុក​ក្ដារចុចនេះ​នៅ​លើ​ឧបករណ៍​ផ្សេង"; diff --git a/ios/engine/KMEI/KeymanEngine/Classes/de.lproj/ResourceInfoView.strings b/ios/engine/KMEI/KeymanEngine/Classes/de.lproj/ResourceInfoView.strings index aa12fd1fb3..e6af056038 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/de.lproj/ResourceInfoView.strings +++ b/ios/engine/KMEI/KeymanEngine/Classes/de.lproj/ResourceInfoView.strings @@ -1,3 +1,2 @@ - /* Class = "UILabel"; text = "Scan this code to load this keyboard on another device"; ObjectID = "z2O-MT-IoV"; */ -"z2O-MT-IoV.text" = "Scan this code to load this keyboard on another device"; +"z2O-MT-IoV.text" = "Scannen Sie diesen Code, um diese Tastatur auf einem anderen Gerät zu laden"; diff --git a/ios/engine/KMEI/KeymanEngine/Classes/fr.lproj/ResourceInfoView.strings b/ios/engine/KMEI/KeymanEngine/Classes/fr.lproj/ResourceInfoView.strings index aa12fd1fb3..5b0e4ea151 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/fr.lproj/ResourceInfoView.strings +++ b/ios/engine/KMEI/KeymanEngine/Classes/fr.lproj/ResourceInfoView.strings @@ -1,3 +1,2 @@ - /* Class = "UILabel"; text = "Scan this code to load this keyboard on another device"; ObjectID = "z2O-MT-IoV"; */ -"z2O-MT-IoV.text" = "Scan this code to load this keyboard on another device"; +"z2O-MT-IoV.text" = "Scannez ce code pour charger ce clavier sur un autre appareil"; diff --git a/ios/engine/KMEI/KeymanEngine/Classes/km.lproj/ResourceInfoView.strings b/ios/engine/KMEI/KeymanEngine/Classes/km.lproj/ResourceInfoView.strings index aa12fd1fb3..10e17b7cc9 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/km.lproj/ResourceInfoView.strings +++ b/ios/engine/KMEI/KeymanEngine/Classes/km.lproj/ResourceInfoView.strings @@ -1,3 +1,2 @@ - /* Class = "UILabel"; text = "Scan this code to load this keyboard on another device"; ObjectID = "z2O-MT-IoV"; */ -"z2O-MT-IoV.text" = "Scan this code to load this keyboard on another device"; +"z2O-MT-IoV.text" = "ស្កេនកូដ​នេះដើម្បី​ផ្ទុក​ក្ដារចុចនេះ​នៅ​លើ​ឧបករណ៍​ផ្សេង"; From 51f9bdc9a73f75a61cfbbdfef2163f9e068dcf3c Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 4 Mar 2021 10:26:03 +0700 Subject: [PATCH 100/125] docs(ios): about the ios l10n updating process --- resources/build/l10n/README.md | 21 ++++++++++++++++-- .../build/l10n/imgs/updating-ios-l10ns-1.png | Bin 0 -> 1310709 bytes .../build/l10n/imgs/updating-ios-l10ns-2.png | Bin 0 -> 125588 bytes 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 resources/build/l10n/imgs/updating-ios-l10ns-1.png create mode 100644 resources/build/l10n/imgs/updating-ios-l10ns-2.png diff --git a/resources/build/l10n/README.md b/resources/build/l10n/README.md index 318e6815e3..7a3023a51a 100644 --- a/resources/build/l10n/README.md +++ b/resources/build/l10n/README.md @@ -6,10 +6,10 @@ Downloading and updating files between Keyman and Crowdin happens automatically on GitHub by way of the Crowdin git integration. The configuration file for all platforms is a YAML file named `/crowdin.yml`. Currently, the git integration tracks the `master` branch. -The only thing that needs to be done manually is to update `crowdin.yml` if new files get added +For most of our platforms, the only thing that needs to be done manually is to update `crowdin.yml` if new files get added (and of course translating the strings on the [Crowdin website](https://crowdin.com/project/keyman)). -## Manual up- and download with Crowdin CLI +## Manual upload and download with Crowdin CLI The following describes how alternatively the Crowdin CLI (v3) tool could be used to automate downloading and updating files between Keyman and Crowdin. @@ -92,6 +92,23 @@ android/KMEA/app/src/main/java/com/tavultesoft/kmea/DisplayLangugages.java For the BCP-47 language tags to use in that file, don't include script names since the Android locales only handle language ID and region. +### Updating Localization Targets in Keyman Engine / the Keyman App for iPhone and iPad + +When a language receives its first localization for our iOS platform, a few settings must be +tweaked within Xcode in order to enable it. + +1. For both the KeymanEngine and Keyman subprojects, go to the project page's info tab and inform Xcode of the appropriate language code. + +![Refer to [this image](imgs/updating-ios-l10ns-1.png) for guidance.](./imgs/updating-ios-l10ns-1.png) + +Note that Xcode will usually fail to actually _include_ the newly downloaded resources. + +2. So, to include those resources, search the workspace for each localized file and select them one at a time. When you do so, the right-hand side bar should a set of checkboxes for each localized language, with the new language unchecked. + +![[This image](imgs/updating-ios-l10ns-2.png) should provide a helpful reference.](./imgs/updating-ios-l10ns-2.png) + +Clicking the checkbox will summon a dialog that will allow the existing file to be utilized. + ## Tip Microsoft offers a website that allows to check how terms used in Windows are translated diff --git a/resources/build/l10n/imgs/updating-ios-l10ns-1.png b/resources/build/l10n/imgs/updating-ios-l10ns-1.png new file mode 100644 index 0000000000000000000000000000000000000000..ddcd11d4d12234fb3704f74ccb212bfc5c343b10 GIT binary patch literal 1310709 zcmeFZdpuNc8#k&Hl~5@XnWRF=I8?$gl}e?ORLEgOlAK~3!>pu2VUkk^OeLg3a?WAM zF^3r;=Zy0>592WAwANlj{hsG}-@W%||FPfqvp=8x_%_xuv+ny|>$>mzI()Bd3cYku zUwFfg4Salj!e`H%ybK=-`1k~K)(ODxD1MJQ%*VI!rh}f|rL%f^yDquA-g0oV%dUX!zkAOne19HX(2n&?OR!L432!%g2q;&2?U+*e(lWSQYtY(Yx&QLRy|?{co|8lHJ4ci>;!nvk~sv3#!jcJQ`{rU}q zTYk9>NNRG{yN&zI?A|0?uxsm=`&;tPtMCu;oz-`>MRJm5)(iUY66`#-#^TVcen~|= zxywEm_^SF0&+LA){iTH3q5Z2*U4m|${8akw{^z$p-@TPA?2{ex-?WzA=Q`Y^G*)}( zN8hT}Gho&?zUo(kukTC}v_x7@Iv%RKK~ze(VPLaeHG2Emjq+Lp`+RmylSb?%2>bLM+ktoAFHF>Vlo~V~>LSRyAqQhpD@X z^*!+W?s?Q_lSQY{ox)cIR|~Vj3yE@){@1YKJF8+ITt02|Kv4ukYV~Sf+%fy^<0y5( zsOFXC<*U=RwQ&mVZB_Dr!S@ z#ErtDZ$+!#+)Mp{Fh;)6qiuNTyBL-6W-T8@?L!0je$br%?x-wZm8-ALCt+mMKK>mi>oj&})rj6gr)@%~ma(w>_feEF@Y%`Ht4+^a} zE36&1-exLz_~t1MP&^n+sphQ|;-YXvr~k(Kp$7`Rq^nL2CGo(e2$@+Zs;`HQ9Kq|F$~)gkw|5ZT@@GI>A=Qi<;zb zqXkqBhJ~8hC~ubbQpKLZuhA-x?mIstH?uBkedoiM4=k*WcD> zhMe3Tb=z=0(A##uYVgtEg7|`$1^A8=lY4sQ7o*PXv3;(tcz>(GK-7k_$?t{Vm%O)q z|Ma~}v(#7d&IgkhMdVW>lZUpr@AVaBDlm65H~ZdyXONY=WMrdwqRy%AT%E`{^%o9Ok_-03?d$zv_? zw=J$~T{vrgy!({n+uv_W-n5K)&0F8wQSz+xft3OJI8hHsYHX7E)Yu|^2icU-=F(L9 zfqNzRo$aTJpD$C4KO3iEk5P*+W-6a*zZmzazC9t+Jl*dM@ILq>4-C&*I?qzi^7>-ixtv5Of#K~GK-I}zHzv&^hNqAD&M?h0xSb!qsv3*{0 zTw z@B;OPi-xntl!m&-QeOggx zdo`=eG$1x5HZt~Hy>sl&SpI8^*UnzsdyW0s#oRc(v(2o{G2J|!?WE+iud5+9Et}aG zYg3=Mtve%M$Bx}a>Q%0qb}TwwRI_+`5yiG)Yq8BmJc4%xuMXZ3Tpw{#KhHK}@K%k` z#Sc+&Q5|VrW5w#@lT%7=y^}qfk5#I@!#vJ6zHn4NBoSc{X^>F=JF6k#Ntk%#~- z(k+Xwf?tCd!`Y+b!>_2{Di%^+mv*no-;#eTpE`YUdNtz?qh7`Xe;2=2=7J0vm7sGf z(HxcOr&_%4Ucl6^@rCm48uMvO+V4Tz!KnW1AJiS)_jlG>4bdD zTMCXy^r&1r z2;nBJsTp4qpDLRnOMez5n^}nK@$HGdq;zO^j#7@?d9;$ElWj-B-A^-5W zdPmE4AsgQ;M;q6g&YEqNTvX^$MVHG9?}KMuX0d~YdsVii$WNa2iP`?VKyCKcbG_$k zX@||^HFXM)Fg?%RK6mO|`;c1{^@48Po#*?{I-iqE-kUmgeuA*&h~4k#X^+hL=J`*? zQ>mm^_inxVkyGrTpE7ZtYKVSzy!OnGj;Ps0M15vI!=A3DVYj%x;K9NJ`R~^K*Kgnz zj$M(x7rpJ-)yRdT+PnNNc;Szpu~3|jxw}_tFWG%JHgBYu6yEqdM*BtF^L!*Bo)8I~ z`G{>BKK>UEOkA;vU{RO~z#$$U9yE@6r^{rbl5 zM&xPIA)?(SAMFtYcSfw*!ZbhXmNGr237-t35B10|k{rB}Y@*uJd! zDqOD{yS|HVjS43X<4MJF=f6%2r{ zLLGOseQk?P4=8kDeN&G~j4^LFZa>iOef5;dVdpQMU+z?}!w14mKI(mBIII26acY@p z{N5;$zs5Cf1=2BFU5)?o|XQ#IwQqh+s*IiVzBme?|4=O*K+D&RKl<}dh+@7 zc-Ky1QF>cyo)>;BF~;+D!q*IYS5~hOB^XD!Lc2_KMqn{a(LTXpQGoMecJLS1h;1(V zMYI*V$!m+W9r-W!Pj4NUyFjMMXF`+K~WQgD(Wj;VPW)n zQc%qk{6c>1^?P`G1d*auvM>_*LtZ)m!kvkulAiQ=WIr+fXqBetq(|9Ug*}HPtyQnv z?L%T)=oOi;%P}Q5osRzBKkxQZ{ae;jgc=hDweeOPzxi-3k0xW3=adOWTd>J}{&KYHY|0ayR6*g4c>lFd=il8i-sfqpoarTYsWL4?&RdU`M=>byo-6GG zqXeG_(u(3x@6n(AZ_a*L%wi8#==AGIfG_C!46)9rz}{x`0@yR(+COFptc8B{Y7}O& zUvfJ8=7y1Bn8|x%%&)oV42blJW5S$5o4>L&b$6AFSjq>ozuton{T_yF;`RP>(eEiGrujb9z<@Ljd z^3j4%o$Wks?DBDTa=~i(=0(rFHq_=|9ckzjSur_VBo?g+zLLdnSpOyQx^KtmsNG{kv zj|D#6@4yrRB-!Z$6vdCWlL$7N|ckr?*jP597QB zqcAn`TO^~gq3&b;$6k!#{E#26Dzt!#9jpvJYNkn{aQ1Bo+_dh$|Nbk1|4QKhu>=Yt z%3SlODb9#PyY?n`;(*(2ozl2^1-!XoicMhj8EW z@qPU*p!UpaZIz;6b5Jmvj%E@#u2gcr&<9UY38e*KboHO2pAes*pAzfQb!Ze)Q>UI- zhps1@NUs;6mi**a1YTga2d+Krw6^)y^pu>>k_RZ`6KF{_q>@Vlq9D6CC8-~fjOnF% zNEz^VC_}k%Aq7JV+xZ67PwdL-NKu>F9_;p;^JTT{_AA6mQZURxz%DSD|!o<2# zhJen!^{44XLGe~PQ3y{P=9n3_5M?*5mYorkwSK0>?-QFkxc5w84gZffkMaV-DC3mJ zUhWnF(a)T3mM5J*H+;D(Q@U&MN51D&KsG_Q)^|MVp_95iS1_edHY3_*e?-nyl