Merge branch 'epic/kmc-convert' into feat/developer/kmc-convert
Some checks are pending
Keyman Build Summary / Summarize build status checks (push) Waiting to run

This commit is contained in:
SabineSIL 2026-03-16 14:23:32 +01:00 • committed by GitHub
commit 2faf0a78e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
142 changed files with 4097 additions and 1723 deletions

View file

@ -1,5 +1,65 @@
# Keyman Version History
## 19.0.214 alpha 2026-03-12
* chore(linux): Update debian changelog (#15718)
* maint(resources): use x86_64 emscripten on arm/windows builds (#15715)
## 19.0.213 alpha 2026-03-10
* maint(linux): set git credential for linux help on the correct repo (#15707)
## 19.0.212 alpha 2026-03-09
* chore(android): Cleanup unused Extracted Text (#15671)
* chore(deps): bump fast-xml-parser from 5.3.4 to 5.3.8 (#15649)
* fix(developer): resolve input project path for `kmc copy` (#15700)
* fix(developer): await copier result before reporting success or failure (#15701)
* fix(developer): correct INVALID_HANDLE_VALUE check in UnlockProject (#15704)
* fix(developer): use trimmed version of paths when verifying in New Project (#15705)
## 19.0.211 alpha 2026-03-06
* chore(deps-dev): bump basic-ftp from 5.0.5 to 5.2.0 (#15631)
* chore(deps-dev): bump rollup from 4.22.4 to 4.59.0 (#15632)
* chore(deps): bump minimatch and @microsoft/api-extractor (#15647)
* chore(deps): bump minimatch from 10.1.1 to 10.2.4 in /developer/src/server/src/win32/trayicon/addon-src (#15648)
* chore(deps): bump tar from 7.5.7 to 7.5.10 in /developer/src/server/src/win32/trayicon/addon-src (#15682)
* chore(deps): bump multer from 2.0.2 to 2.1.1 (#15681)
* chore(web): remove implicit any type when setting processingConfig.itemIdentifier (#15689)
* maint(common): rollback from Typescript 5.8.2 to 5.4.x (#15690)
* docs: tweak build bot docs and update imsample gitignore (#15691)
* maint(developer): make `TestCompilerCallbacks` usage more consistent and simplify (#15665)
* maint(developer): cleanup additional TestCompilerCallbacks references (#15684)
* fix(developer): prevent new projects from overwriting existing projects (#15669)
* fix(developer): retrieve debug memo anchor without side effects (#15672)
* fix(developer): ngrok upgrade (#15626)
* chore(deps-dev): bump minimatch from 9.0.5 to 9.0.9 in /resources/build/pr-build-status (#15650)
## 19.0.210 alpha 2026-03-05
* fix(developer): apply keyboard description in Windows keyboard import (#15667)
* fix(developer): improve UI feedback on upgrade (#15670)
## 19.0.209 alpha 2026-03-04
* maint(ios): update fv Cartfile for Xcode 26 (#15655)
* feat(linux): implement diagnostic report (#15576)
* refactor(core): cleanup (#15656)
## 19.0.208 alpha 2026-03-03
* docs(core): add keyhandling doc (#15629)
* fix(developer): handle CRLF as CR internally in LDML debugger (#15616)
* fix(developer): handle .kps file that has no `<Package>` element (#15622)
* fix(developer): use correct return values and nullish checks in package-validation (#15653)
## 19.0.207 alpha 2026-03-02
* chore(ios): Cleanup `RELEASE_OEM_FIRSTVOICES` var in build.sh scripts (#15605)
* fix(developer): package compiler handle missing `info` section in .kps file (#15620)
* fix(developer): handle window already destroyed when closing editor (#15618)
## 19.0.206 alpha 2026-02-25
* maint(ios): upgrade to Xcode 26.2 (#15498)
@ -1223,6 +1283,30 @@
* refactor(windows): rename `TKeymanMutex.MutexOwned` to `TakeOwnership` and add `ReleaseOwnership` (#13168)
* chore: increment to alpha 19.0 (#13187)
## 18.0.248 stable 2026-03-12
* fix(ios): Revert Sentry to 8.38.0 to fix FirstVoices crash on startup with 18.0.247 (#15726)
## 18.0.247 stable 2026-03-09
* chore(linux): Update debian changelog (#15556)
* chore(linux): update copyright year and standards version (#15558)
* fix(mac): retry for mac disk detach (#15577)
* maint(mac): upgrade to Xcode 26.2 (#15575)
* maint(ios): upgrade to Xcode 26.2 (#15614)
* fix(developer): handle CRLF as CR internally in LDML debugger (#15660)
* fix(developer): handle .kps file that has no `<Package>` element (#15661)
* fix(developer): use correct return values and nullish checks in package-validation (#15662)
* fix(developer): handle window already destroyed when closing editor (#15663)
* fix(developer): package compiler handle missing `info` section in .kps file (#15664)
* fix(developer): apply keyboard description in Windows keyboard import (#15668)
* fix(developer): improve UI feedback on upgrade (#15686)
* fix(developer): prevent new projects from overwriting existing projects (#15694)
* fix(developer): retrieve debug memo anchor without side effects (#15695)
* fix(developer): ngrok upgrade (#15633)
* fix(developer): await copier result before reporting success or failure (#15702)
* fix(developer): resolve input project path for `kmc copy` (#15703)
## 18.0.246 stable 2026-02-04
* cherrypick(common): Update FirstVoices versions in keyboards.csv (#15273)

View file

@ -1 +1 @@
19.0.207
19.0.215

View file

@ -43,7 +43,6 @@ import io.sentry.Sentry;
public class SystemKeyboard extends InputMethodService implements OnKeyboardEventListener {
private static View inputView = null;
private static ExtractedText exText = null;
private KMHardwareKeyboardInterpreter interpreter = null;
private int inputType = InputType.TYPE_NULL;
private int lastOrientation = Configuration.ORIENTATION_UNDEFINED;
@ -204,10 +203,9 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
return value, so we test for that as well (#11479)
*/
if (icText != null && icText.text != null) {
boolean didUpdateText = KMManager.updateText(KeyboardType.KEYBOARD_TYPE_SYSTEM, icText.text.toString());
boolean didUpdateSelection = KMManager.updateSelectionRange(KeyboardType.KEYBOARD_TYPE_SYSTEM);
if (!didUpdateText || !didUpdateSelection)
exText = icText;
// Update the text selection but ignore the returned statuses
KMManager.updateText(KeyboardType.KEYBOARD_TYPE_SYSTEM, icText.text.toString());
KMManager.updateSelectionRange(KeyboardType.KEYBOARD_TYPE_SYSTEM);
}
}
@ -278,10 +276,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
@Override
public void onKeyboardLoaded(KeyboardType keyboardType) {
if (keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
if (exText != null)
exText = null;
}
// Do nothing
}
@Override

View file

@ -36,9 +36,7 @@
"restructure": "3.0.1"
},
"devDependencies": {
"@types/mocha": "^5.2.7",
"@types/node": "^20.4.1",
"ajv": "^8.17.1",
"ajv": "^8.12.0",
"ajv-cli": "^5.0.0",
"ajv-formats": "^2.1.1",
"c8": "^7.12.0",

View file

@ -303,7 +303,7 @@ end;
function TframeCEFHost.HasFocus: Boolean;
begin
AssertVclThread;
Result := cefwp.HandleAllocated and IsChild(cefwp.Handle, GetFocus);
Result := Assigned(cefwp) and cefwp.HandleAllocated and IsChild(cefwp.Handle, GetFocus);
end;
procedure TframeCEFHost.Handle_CEF_SHOW(var message: TMessage);
@ -373,7 +373,7 @@ end;
procedure TframeCEFHost.SetFocus;
begin
AssertVclThread;
if not FIsClosing and cefwp.CanFocus and Assigned(cef) then
if not FIsClosing and Assigned(cefwp) and Assigned(cef) and cefwp.CanFocus then
begin
GetParentForm(Self).ActiveControl := Self;
cef.SetFocus(True);
@ -574,7 +574,8 @@ end;
procedure TframeCEFHost.Handle_CEF_DESTROY(var Message: TMessage);
begin
AssertVclThread;
cefwp.DestroyChildWindow;
if Assigned(cefwp) then
cefwp.DestroyChildWindow;
FreeAndNil(cefwp);
end;

View file

@ -19,6 +19,7 @@ This is an internal API intended for use only within Keyman Engine.
* [Options](options)
* [Processor](processor)
* [State and Actions](state)
* [Key Handling](keyhandling)
* [JSON introspection Schema](json-schema)
* [Building Keyman Core](building)
@ -122,4 +123,4 @@ Caps Lock.
[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
[km_core_event]: processor#km_core_event "km_core_event function"
[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"

View file

@ -4,10 +4,10 @@ title: JSON Introspection Schema
The [`km_core_state_to_json()`](state#km_core_state_to_json) call
generates a JSON document describing the internal state of the keyboard
processor, this is the schema describing that document.
processor. This is the schema describing that document.
**WARNING**: The structure and format of the JSON document is independently
versioned is not considered part of C API. It is intended solely for use in
versioned and is not considered part of the C API. It is intended solely for use in
diagnostics or by development and debugging tools which may need to be aware of
keyboard processor engine implementation details.
@ -74,4 +74,4 @@ keyboard processor engine implementation details.
"context": { "$ref": "#/definitions/context" }
}
}
```
```

View file

@ -0,0 +1,42 @@
# Key Handling
For each key press the processor will called twice, for the key down event
as well as for the key up event. Depending on the type of key pressed the
processor might handle the key itself or pass it on to the application.
The value of `emit_keystroke` in `km_core_actions` struct tells if the
processor handled the key (`emit_keystroke=0`) or not (`emit_keystroke=1`).
It is important that the same value gets set for both key down and key up
events, otherwise the application might miss some events which then looks
to the user like keys are stuck.
Usually the processor won't handle any frame keys and let the application
deal with it. This allows shortcut keys like <kbd>Ctrl</kbd>+<kbd>C</kbd>
to work. The only exception is the <kbd>Backspace</kbd> key which is
handled internally if enough context is available. Regular keys will be
handled by the processor.
Note that there is a difference between CLDR/LDML and KMN keyboards if
there are no rules/transforms defined for a key: KMN keyboards will
output the cap value of the key (e.g. pressing <kbd>a</kbd> will output
`a` if no rule is defined), whereas CLDR/LDML keyboards will suppress
any output for that key.
The following table lists the state of `km_core_actions.emit_keystroke`
on return of `km_core_process_event` when the following type of key is
pressed:
|Type | KeyDown | KeyUp |
|-----------------------------|---------------|----------|
|Key with rule | FALSE | FALSE |
|Key w/o rule | FALSE | FALSE |
|Framekeys: | | |
|<kbd>Enter</kbd> | TRUE | TRUE |
|<kbd>Backspace</kbd>¹ | FALSE | FALSE |
|<kbd>Backspace</kbd>² | TRUE | TRUE |
|<kbd>Ctrl</kbd>+Key | TRUE | TRUE |
|Modifier key <kbd>Shift</kbd>| TRUE | TRUE |
|Modifier key <kbd>Ctrl</kbd> | TRUE | TRUE |
|Modifier key <kbd>LAlt</kbd> | TRUE | TRUE |
1: context available<br/>
2: without or with empty context

View file

@ -3,7 +3,7 @@ title: Options - Keyman Core API
---
A state’s default options are set from the keyboard at creation time and the
environment. The Platform layer is then is expected to apply any persisted
environment. The Platform layer is then expected to apply any persisted
options it is maintaining. Options are passed into and out of API functions as
simple C arrays of [km_core_option_item] terminated with a `KM_CORE_OPTIONS_END`
sentinel value. A state's options are exposed and manipulatable via the
@ -281,4 +281,4 @@ km_core_state_options_to_json(km_core_state const *state,
[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
[km_core_event]: processor#km_core_event "km_core_event function"
[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"

View file

@ -15,8 +15,8 @@ enum km_core_event_flags {
KM_CORE_EVENT_FLAG_DEFAULT = 0,
KM_CORE_EVENT_FLAG_TOUCH = 1,
};
```
## Values
`KM_CORE_EVENT_FLAG_DEFAULT`
@ -195,4 +195,4 @@ enum km_core_event_code {
[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
[km_core_event]: processor#km_core_event "km_core_event function"
[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"

View file

@ -15,7 +15,8 @@ owned by the state object.
## Description
Describes the
Describes a change to the hardware caps lock indicator state requested by
Keyman Core to the Platform layer.
## Specification
```c
@ -50,6 +51,7 @@ This API replaces the Action items APIs, which are now deprecated and will be
removed in the future.
## Specification
```c
typedef struct {
unsigned int code_points_to_delete;
@ -60,8 +62,8 @@ typedef struct {
km_core_caps_state new_caps_lock_state;
const km_core_usv* deleted_context;
} km_core_actions;
```
## Members
`code_points_to_delete`
@ -78,15 +80,17 @@ typedef struct {
`emit_keystroke`
: Emit the (unmodified) input keystroke to the application, 0 = no, 1 = yes.
On most platforms this signals whether the processor handled the event
(0) or not (1). See also [key handling](keyhandling).
`new_caps_lock_state`
: -1=unchanged, 0=off, 1=on
`deleted_context`
: Reference copy of actual UTF32 codepoints deleted from end of context
(closest to caret) exactly code_points_to_delete in length (plus null
(closest to caret) exactly `code_points_to_delete` in length (plus null
terminator). Used to determine encoding conversion differences when
deleting; only set when using [km_core_state_get_actions], otherwise nullptr.
deleting; only set when using [km_core_state_get_actions], otherwise `nullptr`.
-------------------------------------------------------------------------------
@ -282,4 +286,4 @@ An opaque pointer to a state object.
[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
[km_core_event]: processor#km_core_event "km_core_event function"
[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"

View file

@ -576,15 +576,17 @@ typedef struct {
`emit_keystroke`
: Emit the (unmodified) input keystroke to the application, 0 = no, 1 = yes.
On most platforms this signals whether the processor handled the event
(0) or not (1). See also [key handling](keyhandling).
`new_caps_lock_state`
: -1=unchanged, 0=off, 1=on
`deleted_context`
: Reference copy of actual UTF32 codepoints deleted from end of context
(closest to caret) exactly code_points_to_delete in length (plus null
(closest to caret) exactly `code_points_to_delete` in length (plus null
terminator). Used to determine encoding conversion differences when
deleting; only set when using [km_core_state_get_actions], otherwise nullptr.
deleting; only set when using [km_core_state_get_actions], otherwise `nullptr`.
-------------------------------------------------------------------------------
@ -744,7 +746,7 @@ title: Options - Keyman Core API
---
A state’s default options are set from the keyboard at creation time and the
environment. The Platform layer is then is expected to apply any persisted
environment. The Platform layer is then expected to apply any persisted
options it is maintaining. Options are passed into and out of API functions as
simple C arrays of [km_core_option_item] terminated with a `KM_CORE_OPTIONS_END`
sentinel value. A state's options are exposed and manipulatable via the

View file

@ -243,7 +243,7 @@ void ldml_event_state::emit_backspace() {
// else loop again
assert(end.type != KM_CORE_CT_END); // inappropriate here.
state->context().pop_back();
}
}
/*
We couldn't find a character at end of context (context is empty),
so we'll pass the backspace keystroke on to the app to process; the
@ -532,10 +532,10 @@ ldml_event_state::ldml_event_state(
uint8_t i,
uint16_t e) {
this->state = s;
this->vk = v;
this->modifier_state = m;
this->is_key_down = i;
this->event_flags = e;
this->_vk = v;
this->_modifier_state = m;
this->_is_key_down = i;
this->_event_flags = e;
actions.persist_options = new km_core_option_item[1];
actions.persist_options[0] = NULL_OPTIONS[0];

View file

@ -178,10 +178,10 @@ public:
size_t context_to_string(std::u32string &str, bool include_markers = true);
private:
km_core_virtual_key vk;
uint16_t modifier_state;
uint8_t is_key_down;
uint16_t event_flags;
km_core_virtual_key _vk;
uint16_t _modifier_state;
uint8_t _is_key_down;
uint16_t _event_flags;
km_core_state *state;
// our in-flight action struct.
@ -194,12 +194,12 @@ public:
// implementation
km_core_virtual_key
ldml_event_state::get_vk() const {
return vk;
return _vk;
}
uint16_t
ldml_event_state::get_modifier_state() const {
return modifier_state;
return _modifier_state;
}
} // namespace core

View file

@ -18,7 +18,7 @@ protected:
km_core_keyboard_dispose(this->keyboard);
this->keyboard = nullptr;
}
}
}
};
TEST_F(KmCoreKeyboardApiTests, LoadFromBlob) {

View file

@ -239,7 +239,7 @@ kbd_log = custom_target(test_kbd + '.kmx'.underscorify(),
input: kbd_src,
command: kmc_cmd + ['build', '--debug', '--no-compiler-version', '@INPUT@', '--out-file', kbd_obj]
)
test('imx_list', imx_e, depends: kbd_log, args: [kbd_obj] )
test('imx_list', imx_e, depends: kbd_log, args: [kbd_obj] )
external_e = executable('ext_event_tests', ['kmx_external_event.tests.cpp', common_test_files],
cpp_args: defns + warns,
@ -258,4 +258,4 @@ kbd_log = custom_target(test_kbd + '.kmx'.underscorify(),
input: kbd_src,
command: kmc_cmd + ['build', '--debug', '--no-compiler-version', '@INPUT@', '--out-file', kbd_obj]
)
test('ext_event', external_e, depends: kbd_log, args: [kbd_obj] )
test('ext_event', external_e, depends: kbd_log, args: [kbd_obj] )

View file

@ -125,42 +125,73 @@ Update database
![Options dialog - Server tab](../images/ui/frmOptions_Server.png)
There are two configurations in the Keyman Developer Server section:
1. Configure Server...
2. List local URLs for Server
### Configure Server dialog
Configure Server...
After clicking the **Configure Server...** button, a new window will pop-up:
: After clicking the button, a new window will pop-up
![Keyman Developer Server Options dialog](../images/ui/frmKeyman_Developer_Server_Options.png)
![Keyman Developer Server Options dialog](../images/ui/frmKeyman_Developer_Server_Options.png)
You can customize the Keyman Developer Server for testing your keyboard online
and on other devices. Features of the Keyman Developer Server include:
You can customize the Keyman Developer server for testing the usability of your keyboard. Features of the Keyman Developer server include:
* Seamless integration with the IDE (Start, Stop, Live reload, Recompile...)
* Sharing keyboards for testing online and on other devices
* Allowing testing across devices and networks
* Seamless integration with the IDE (Start, Stop, Live reload, Recompile...)
* Port configuration
* Allow testing cross devices
* Wider range support for NGROK...
Keyman Developer Server integrates with a free tool called
[ngrok](https://ngrok.com) that provides a public URL to your instance of Keyman
Developer Server. This URL is dynamic and can change over time, and is only
available while Keyman Developer Server is running. This URL can be accessed
from any mobile device that is online, whether or not it is on your local
network. This means you can also quickly share keyboards, packages, and models
with other users, with a minimum of difficulty.
[Find out more](https://github.com/keymanapp/keyman/pull/6073)
* [Learn more about ngrok](server#ngrok)
Understanding the three tick boxes will enhance the usability of Keyman Developer:
- **Leave Server running after closing IDE**: allows keyboard testing on local URLs after closing Keyman Developer.
- **Use ngrok to provide public url for web debugger**: configure NGROK as instructed, once it looks something like this:
![Keyman Developer NGROK](../images/ui/frmNGROK_Config.png)
**Options available in this dialog:**
Please restart the machine, and the URL (*link ending in .ngrok-free.app*) will appear in the specified locations:
![Keyman Developer NGROK URL 1](../images/ui/frmNGROK_Server_1.png)
![Keyman Developer NGROK URL 2](../images/ui/frmNGROK_Server_2.png)
- **Leave Server running after closing IDE**: allows keyboard testing on local
URLs even if Keyman Developer is not running.
Now, freely share it across multiple devices using the link or QR code.
![Keyboard Test on Mobile](../images/ui/NGROK_Phone.png)
- **Use ngrok to provide public url for web debugger**: enable ngrok
integration; you may need to download additional components; see below:
- **Show server console window on start**: once ticked, Keyman Developer's server console will appear as an independent window as a node application with the default message:
`Starting Keyman Developer Server 17.0.290-beta, listening on port 8008.`
- **Download or update ngrok**: If the text next to this button shows 'Not
installed', press the button to download the required component
List local URLs for Server
: Below Configure Server, untick the box, and it will quit displaying any local URLs for keyboard testing.
![Keyman Developer Server Options dialog](../images/ui/frmList_Local_URLs.png)
- **Install required redistributable**: ngrok requires an additional Microsoft
VC++ redistributable component; if the text next to this button shows 'Not
installed', press the button and follow the prompts to install the
redistributable component.
Explore more: [Keyman Developer Server](server#toc-configuring-keyman-developer-server)
After enabling ngrok, a URL with ending a domain such as `.ngrok-free.app` will
appear in the Tools/Server menu and the Web and Mobile debugger pages in the
Keyboard Editor and the Package Editor:
![Tools/Options/Server menu showing ngrok](../images/ui/tools_options_server_ngrok.png)
![Web and Mobile test showing ngrok](../images/ui/web_and_mobile_test_ngrok.png)
Once this is active, you can access Keyman Developer Server across multiple
devices and networks using the link or QR code.
- **Show server console window on start**: once ticked, Keyman Developer's
server console will appear as an independent window as a node application,
which may be helpful if you encounter errors as they will appear in the
console.
A default message will appear when the server console starts, similar to:
`Starting Keyman Developer Server <version>-beta, listening on port 8008.`
### List local URLs for Server
When selected, Keyman Developer will show you more local URLs in the
Tools/Server menu for accessing Keyman Developer Server from your browser. Some
of these URLs will work only on your local computer, and will not work on other
devices. Deselect this option in order to show only the most relevant access
URLs. (On some networks, it may be helpful to see all the available URLs.)
## Related links
* [Keyman Developer Server](server#toc-configuring-keyman-developer-server)

View file

@ -296,7 +296,7 @@ context as well.
The devices listed are representative of the various types of platforms that
Keyman supports, to allow testing on each of the available form factors.
## Making your Keyman Developer Server instance globally available with ngrok
## Making your Keyman Developer Server instance globally available with ngrok {#ngrok}
One of the fiddliest parts of testing keyboards and models with mobile devices
is transferring the files to your phone or tablet. Keyman Developer Server will
@ -324,28 +324,30 @@ configure ngrok manually.
click **Configure Server...**. You should be presented with a window similar
to this:
![](images/config.png)
![](../images/ui/frmKeyman_Developer_Server_Options.png)
2. Tick the box **Use ngrok to provide public url for Server**
3. If ngrok is available, a version number will be shown to the right of the
3. If ngrok is available, the text 'Installed' will be shown to the right of the
**Download or update ngrok** button. Otherwise, click this button to download
the latest version.
4. You'll need a free ngrok account, so click the **Create free ngrok
4. **Install required redistributable**: ngrok requires an additional Microsoft
VC++ redistributable component; if the text next to this button shows 'Not
installed', press the button and follow the prompts to install the
redistributable component.
5. You'll need a free ngrok account, so click the **Create free ngrok
account...** button to open the ngrok website, and follow the steps there
to finish the account creation process.
5. After creating the account, you should be presented with an authentication
6. After creating the account, you should be presented with an authentication
token. You can also access this by clicking the **Get token** link in the
Keyman Developer Server Options dialog box. Paste this token into the
**Authentication token** edit box.
6. Select the most appropriate region for your location, to minimize the latency
(delay) when using the ngrok service.
7. Click **OK** to save changes, and start the ngrok service within Keyman
Developer Server.
Once ngrok is configured, you should see an additional **Server** menu item in
Keyman Developer's **Tools** menu, with three items:
**Open `https://some-url.ngrok.io` in browser**
**Open `https://some-url.ngrok-free.app` in browser**
: Opens the public URL to your current Keyman Developer Server instance in the
default browser on your computer.
@ -357,15 +359,7 @@ Keyman Developer's **Tools** menu, with three items:
**Configure...**
: Opens the Keyman Developer Server Options dialog box.
# Configuring Keyman Developer Server
Keyman Developer Server has a number of configuration options. On Windows, using
Keyman Developer is the easiest way to manage these options. Open **Tools |
Options**, select **Server** tab, and click **Configure Server...**.
![](images/config.png)
: Opens the [Keyman Developer Server Options](options#server-tab) dialog box.
## Configuration Files

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View file

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View file

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View file

@ -21,6 +21,7 @@ interface
uses
System.Classes,
Winapi.ActiveX,
Winapi.Messages,
Winapi.RichEdit,
Winapi.Windows,
@ -35,7 +36,7 @@ type
TMemoSelection = record
Start, Finish: Integer;
Anchor: Integer;
Caret: Integer; // In a non-empty selection, Anchor is at the other end of the selection
function ToString: string;
end;
@ -43,7 +44,6 @@ type
private
FOnMessage: TKeymanDeveloperDebuggerMessageEvent;
FAllowUnicodeInput: Boolean;
FSelectionChanging: Boolean;
FIsDebugging: Boolean;
procedure SetAllowUnicode(const Value: Boolean);
function GetSelection: TMemoSelection;
@ -60,7 +60,6 @@ type
property AllowUnicode: Boolean read FAllowUnicodeInput write SetAllowUnicode default True;
property OnMessage: TKeymanDeveloperDebuggerMessageEvent read FOnMessage write FOnMessage;
property Selection: TMemoSelection read GetSelection write SetSelection;
property SelectionChanging: Boolean read FSelectionChanging;
property IsDebugging: Boolean read FIsDebugging write FIsDebugging;
end;
@ -69,6 +68,7 @@ procedure Register;
implementation
uses
tom_TLB,
System.SysUtils;
{ TKeymanDeveloperDebuggerMemo }
@ -107,26 +107,29 @@ begin
end;
function TKeymanDeveloperDebuggerMemo.GetSelection: TMemoSelection;
var
Unk: IUnknown;
TextDoc: ITextDocument;
TextSel: ITextSelection;
begin
Result.Anchor := 0;
// EM_GETSEL doesn't tell us the anchor position, but we can figure
// it out with this kludge. I am not aware of side effects from this
// at this time.
SendMessage(Handle, EM_GETSEL, NativeUInt(@Result.Start), NativeUInt(@Result.Finish));
if Result.Start <> Result.Finish then
Result.Caret := Result.Finish;
if Result.Finish <> Result.Start then
begin
// We only need to play the selection test game if there is a non-zero
// selection length
FSelectionChanging := True;
Lines.BeginUpdate;
try
SendMessage(Handle, EM_SETSEL, -1, 0);
SendMessage(Handle, EM_GETSEL, NativeUInt(@Result.Anchor), 0);
SetSelection(Result);
finally
Lines.EndUpdate;
FSelectionChanging := False;
if SendMessage(Handle, EM_GETOLEINTERFACE, 0, LPARAM(@Unk)) <> 0 then
begin
if Unk.QueryInterface(IID_ITextDocument, TextDoc) = S_OK then
begin
TextSel := TextDoc.Selection;
if TextSel <> nil then
begin
if (TextSel.Flags and tomSelStartActive) <> 0 then
begin
Result.Caret := Result.Start;
end;
end;
end;
end;
end;
end;
@ -153,7 +156,7 @@ end;
procedure TKeymanDeveloperDebuggerMemo.SetSelection(
const Value: TMemoSelection);
begin
if Value.Anchor = Value.Start
if Value.Caret = Value.Start
then SendMessage(Handle, EM_SETSEL, Value.Finish, Value.Start)
else SendMessage(Handle, EM_SETSEL, Value.Start, Value.Finish);
end;
@ -174,10 +177,10 @@ begin
end;
{ TMemoSelection }
function TMemoSelection.ToString: string;
begin
Result := Format('%d %d %d', [Start, Finish, Anchor]);
end;
begin
Result := Format('TMemoSelection(start=%d finish=%d caret=%d)', [Start, Finish, Caret]);
end;
end.

View file

@ -44,6 +44,7 @@ contains
TextFileFormat in '..\general\TextFileFormat.pas',
LeftTabbedPageControl in 'LeftTabbedPageControl.pas',
MenuImgList in 'MenuImgList.pas',
StringGridEditControlled in 'StringGridEditControlled.pas';
StringGridEditControlled in 'StringGridEditControlled.pas',
tom_TLB in 'tom_TLB.pas';
end.

View file

@ -139,6 +139,7 @@
<DCCReference Include="LeftTabbedPageControl.pas"/>
<DCCReference Include="MenuImgList.pas"/>
<DCCReference Include="StringGridEditControlled.pas"/>
<DCCReference Include="tom_TLB.pas"/>
<BuildConfiguration Include="Release">
<Key>Cfg_2</Key>
<CfgParent>Base</CfgParent>

View file

@ -0,0 +1,942 @@
unit tom_TLB;
// ************************************************************************ //
// WARNING
// -------
// The types declared in this file were generated from data read from a
// Type Library. If this type library is explicitly or indirectly (via
// another type library referring to this type library) re-imported, or the
// 'Refresh' command of the Type Library Editor activated while editing the
// Type Library, the contents of this file will be regenerated and all
// manual modifications will be lost.
// ************************************************************************ //
// $Rev: 52393 $
// File generated on 4/03/2026 10:48:24 AM from Type Library described below.
// ************************************************************************ //
// Type Lib: C:\Windows\SysWOW64\RICHED20.dll (1)
// LIBID: {8CC497C9-A1DF-11CE-8098-00AA0047BE5D}
// LCID: 0
// Helpfile:
// HelpString:
// DepndLst:
// (1) v2.0 stdole, (C:\Windows\SysWOW64\stdole2.tlb)
// SYS_KIND: SYS_WIN32
// Errors:
// Hint: Symbol 'Type' renamed to 'type_'
// Hint: Parameter 'Unit' of ITextSelection.MoveLeft changed to 'Unit_'
// Hint: Parameter 'Unit' of ITextSelection.MoveRight changed to 'Unit_'
// Hint: Parameter 'Unit' of ITextSelection.MoveUp changed to 'Unit_'
// Hint: Parameter 'Unit' of ITextSelection.MoveDown changed to 'Unit_'
// Hint: Parameter 'Unit' of ITextSelection.HomeKey changed to 'Unit_'
// Hint: Parameter 'Unit' of ITextSelection.EndKey changed to 'Unit_'
// Hint: Member 'End' of 'ITextRange' changed to 'End_'
// Hint: Parameter 'Unit' of ITextRange.Expand changed to 'Unit_'
// Hint: Parameter 'Unit' of ITextRange.GetIndex changed to 'Unit_'
// Hint: Parameter 'Unit' of ITextRange.SetIndex changed to 'Unit_'
// Hint: Parameter 'Unit' of ITextRange.StartOf changed to 'Unit_'
// Hint: Parameter 'Unit' of ITextRange.EndOf changed to 'Unit_'
// Hint: Parameter 'Unit' of ITextRange.Move changed to 'Unit_'
// Hint: Parameter 'Unit' of ITextRange.MoveStart changed to 'Unit_'
// Hint: Parameter 'Unit' of ITextRange.MoveEnd changed to 'Unit_'
// Hint: Parameter 'Unit' of ITextRange.Delete changed to 'Unit_'
// Hint: Parameter 'Type' of ITextRange.ChangeCase changed to 'Type_'
// Hint: Parameter 'Type' of ITextRange.GetPoint changed to 'Type_'
// Hint: Parameter 'Type' of ITextRange.SetPoint changed to 'Type_'
// Hint: Member 'Protected' of 'ITextFont' changed to 'Protected_'
// Hint: Parameter 'Type' of ITextDocument2.GetClientRect changed to 'Type_'
// ************************************************************************ //
{$TYPEDADDRESS OFF} // Unit must be compiled without type-checked pointers.
{$WARN SYMBOL_PLATFORM OFF}
{$WRITEABLECONST ON}
{$VARPROPSETTER ON}
{$ALIGN 4}
interface
uses Winapi.Windows, System.Classes, System.Variants, System.Win.StdVCL, Vcl.Graphics, Winapi.ActiveX;
// *********************************************************************//
// GUIDS declared in the TypeLibrary. Following prefixes are used:
// Type Libraries : LIBID_xxxx
// CoClasses : CLASS_xxxx
// DISPInterfaces : DIID_xxxx
// Non-DISP interfaces: IID_xxxx
// *********************************************************************//
const
// TypeLibrary Major and minor versions
tomMajorVersion = 1;
tomMinorVersion = 0;
LIBID_tom: TGUID = '{8CC497C9-A1DF-11CE-8098-00AA0047BE5D}';
IID_ITextDocument: TGUID = '{8CC497C0-A1DF-11CE-8098-00AA0047BE5D}';
IID_ITextRange: TGUID = '{8CC497C2-A1DF-11CE-8098-00AA0047BE5D}';
IID_ITextSelection: TGUID = '{8CC497C1-A1DF-11CE-8098-00AA0047BE5D}';
IID_ITextFont: TGUID = '{8CC497C3-A1DF-11CE-8098-00AA0047BE5D}';
IID_ITextPara: TGUID = '{8CC497C4-A1DF-11CE-8098-00AA0047BE5D}';
IID_ITextStoryRanges: TGUID = '{8CC497C5-A1DF-11CE-8098-00AA0047BE5D}';
IID_ITextDocument2: TGUID = '{01C25500-4268-11D1-883A-3C8B00C10000}';
IID_ITextMsgFilter: TGUID = '{A3787420-4267-11D1-883A-3C8B00C10000}';
// *********************************************************************//
// Declaration of Enumerations defined in Type Library
// *********************************************************************//
// Constants for enum __MIDL___MIDL_itf_tom_0000_0000_0001
type
__MIDL___MIDL_itf_tom_0000_0000_0001 = TOleEnum;
const
tomFalse = $00000000;
tomTrue = $FFFFFFFF;
tomUndefined = $FF676981;
tomToggle = $FF676982;
tomAutoColor = $FF676983;
tomDefault = $FF676984;
tomSuspend = $FF676985;
tomResume = $FF676986;
tomApplyNow = $00000000;
tomApplyLater = $00000001;
tomTrackParms = $00000002;
tomCacheParms = $00000003;
tomBackward = $C0000001;
tomForward = $3FFFFFFF;
tomMove = $00000000;
tomExtend = $00000001;
tomNoSelection = $00000000;
tomSelectionIP = $00000001;
tomSelectionNormal = $00000002;
tomSelectionFrame = $00000003;
tomSelectionColumn = $00000004;
tomSelectionRow = $00000005;
tomSelectionBlock = $00000006;
tomSelectionInlineShape = $00000007;
tomSelectionShape = $00000008;
tomSelStartActive = $00000001;
tomSelAtEOL = $00000002;
tomSelOvertype = $00000004;
tomSelActive = $00000008;
tomSelReplace = $00000010;
tomEnd = $00000000;
tomStart = $00000020;
tomCollapseEnd = $00000000;
tomCollapseStart = $00000001;
tomClientCoord = $00000100;
tomNone = $00000000;
tomSingle = $00000001;
tomWords = $00000002;
tomDouble = $00000003;
tomDotted = $00000004;
tomDash = $00000005;
tomDashDot = $00000006;
tomDashDotDot = $00000007;
tomWave = $00000008;
tomThick = $00000009;
tomHair = $0000000A;
tomLineSpaceSingle = $00000000;
tomLineSpace1pt5 = $00000001;
tomLineSpaceDouble = $00000002;
tomLineSpaceAtLeast = $00000003;
tomLineSpaceExactly = $00000004;
tomLineSpaceMultiple = $00000005;
tomAlignLeft = $00000000;
tomAlignCenter = $00000001;
tomAlignRight = $00000002;
tomAlignJustify = $00000003;
tomAlignDecimal = $00000003;
tomAlignBar = $00000004;
tomAlignInterWord = $00000003;
tomAlignInterLetter = $00000004;
tomAlignScaled = $00000005;
tomAlignGlyphs = $00000006;
tomAlignSnapGrid = $00000007;
tomSpaces = $00000000;
tomDots = $00000001;
tomDashes = $00000002;
tomLines = $00000003;
tomThickLines = $00000004;
tomEquals = $00000005;
tomTabBack = $FFFFFFFD;
tomTabNext = $FFFFFFFE;
tomTabHere = $FFFFFFFF;
tomListNone = $00000000;
tomListBullet = $00000001;
tomListNumberAsArabic = $00000002;
tomListNumberAsLCLetter = $00000003;
tomListNumberAsUCLetter = $00000004;
tomListNumberAsLCRoman = $00000005;
tomListNumberAsUCRoman = $00000006;
tomListNumberAsSequence = $00000007;
tomListParentheses = $00010000;
tomListPeriod = $00020000;
tomListPlain = $00030000;
tomCharacter = $00000001;
tomWord = $00000002;
tomSentence = $00000003;
tomParagraph = $00000004;
tomLine = $00000005;
tomStory = $00000006;
tomScreen = $00000007;
tomSection = $00000008;
tomColumn = $00000009;
tomRow = $0000000A;
tomWindow = $0000000B;
tomCell = $0000000C;
tomCharFormat = $0000000D;
tomParaFormat = $0000000E;
tomTable = $0000000F;
tomObject = $00000010;
tomMatchWord = $00000002;
tomMatchCase = $00000004;
tomMatchPattern = $00000008;
tomUnknownStory = $00000000;
tomMainTextStory = $00000001;
tomFootnotesStory = $00000002;
tomEndnotesStory = $00000003;
tomCommentsStory = $00000004;
tomTextFrameStory = $00000005;
tomEvenPagesHeaderStory = $00000006;
tomPrimaryHeaderStory = $00000007;
tomEvenPagesFooterStory = $00000008;
tomPrimaryFooterStory = $00000009;
tomFirstPageHeaderStory = $0000000A;
tomFirstPageFooterStory = $0000000B;
tomNoAnimation = $00000000;
tomLasVegasLights = $00000001;
tomBlinkingBackground = $00000002;
tomSparkleText = $00000003;
tomMarchingBlackAnts = $00000004;
tomMarchingRedAnts = $00000005;
tomShimmer = $00000006;
tomWipeDown = $00000007;
tomWipeRight = $00000008;
tomAnimationMax = $00000008;
tomLowerCase = $00000000;
tomUpperCase = $00000001;
tomTitleCase = $00000002;
tomSentenceCase = $00000004;
tomToggleCase = $00000005;
tomReadOnly = $00000100;
tomShareDenyRead = $00000200;
tomShareDenyWrite = $00000400;
tomPasteFile = $00001000;
tomCreateNew = $00000010;
tomCreateAlways = $00000020;
tomOpenExisting = $00000030;
tomOpenAlways = $00000040;
tomTruncateExisting = $00000050;
tomRTF = $00000001;
tomText = $00000002;
tomHTML = $00000003;
tomWordDocument = $00000004;
tomBold = $80000001;
tomItalic = $80000002;
tomUnderline = $80000004;
tomStrikeout = $80000008;
tomProtected = $80000010;
tomLink = $80000020;
tomSmallCaps = $80000040;
tomAllCaps = $80000080;
tomHidden = $80000100;
tomOutline = $80000200;
tomShadow = $80000400;
tomEmboss = $80000800;
tomImprint = $80001000;
tomDisabled = $80002000;
tomRevised = $80004000;
tomNormalCaret = $00000000;
tomKoreanBlockCaret = $00000001;
tomIncludeInset = $00000001;
tomIgnoreCurrentFont = $00000000;
tomMatchFontCharset = $00000001;
tomMatchFontSignature = $00000002;
tomCharset = $80000000;
tomRE10Mode = $00000001;
tomNoIME = $00080000;
tomSelfIME = $00040000;
type
// *********************************************************************//
// Forward declaration of types defined in TypeLibrary
// *********************************************************************//
ITextDocument = interface;
ITextDocumentDisp = dispinterface;
ITextRange = interface;
ITextRangeDisp = dispinterface;
ITextSelection = interface;
ITextSelectionDisp = dispinterface;
ITextFont = interface;
ITextFontDisp = dispinterface;
ITextPara = interface;
ITextParaDisp = dispinterface;
ITextStoryRanges = interface;
ITextStoryRangesDisp = dispinterface;
ITextDocument2 = interface;
ITextDocument2Disp = dispinterface;
ITextMsgFilter = interface;
// *********************************************************************//
// Declaration of structures, unions and aliases.
// *********************************************************************//
wireHWND = ^_RemotableHandle;
POleVariant1 = ^OleVariant; {*}
PInteger1 = ^Integer; {*}
tomConstants = __MIDL___MIDL_itf_tom_0000_0000_0001;
__MIDL_IWinTypes_0009 = record
case Integer of
0: (hInproc: Integer);
1: (hRemote: Integer);
end;
_RemotableHandle = record
fContext: Integer;
u: __MIDL_IWinTypes_0009;
end;
UINT_PTR = LongWord;
LONG_PTR = Integer;
// *********************************************************************//
// Interface: ITextDocument
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {8CC497C0-A1DF-11CE-8098-00AA0047BE5D}
// *********************************************************************//
ITextDocument = interface(IDispatch)
['{8CC497C0-A1DF-11CE-8098-00AA0047BE5D}']
function Get_Name: WideString; safecall;
function Get_Selection: ITextSelection; safecall;
function Get_StoryCount: Integer; safecall;
function Get_StoryRanges: ITextStoryRanges; safecall;
function Get_Saved: Integer; safecall;
procedure Set_Saved(pValue: Integer); safecall;
function Get_DefaultTabStop: Single; safecall;
procedure Set_DefaultTabStop(pValue: Single); safecall;
procedure New; safecall;
procedure Open(const pVar: OleVariant; Flags: Integer; CodePage: Integer); safecall;
procedure Save(const pVar: OleVariant; Flags: Integer; CodePage: Integer); safecall;
function Freeze: Integer; safecall;
function Unfreeze: Integer; safecall;
procedure BeginEditCollection; safecall;
procedure EndEditCollection; safecall;
function Undo(Count: Integer): Integer; safecall;
function Redo(Count: Integer): Integer; safecall;
function Range(cp1: Integer; cp2: Integer): ITextRange; safecall;
function RangeFromPoint(x: Integer; y: Integer): ITextRange; safecall;
property Name: WideString read Get_Name;
property Selection: ITextSelection read Get_Selection;
property StoryCount: Integer read Get_StoryCount;
property StoryRanges: ITextStoryRanges read Get_StoryRanges;
property Saved: Integer read Get_Saved write Set_Saved;
property DefaultTabStop: Single read Get_DefaultTabStop write Set_DefaultTabStop;
end;
// *********************************************************************//
// DispIntf: ITextDocumentDisp
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {8CC497C0-A1DF-11CE-8098-00AA0047BE5D}
// *********************************************************************//
ITextDocumentDisp = dispinterface
['{8CC497C0-A1DF-11CE-8098-00AA0047BE5D}']
property Name: WideString readonly dispid 0;
property Selection: ITextSelection readonly dispid 1;
property StoryCount: Integer readonly dispid 2;
property StoryRanges: ITextStoryRanges readonly dispid 3;
property Saved: Integer dispid 4;
property DefaultTabStop: Single dispid 5;
procedure New; dispid 6;
procedure Open(const pVar: OleVariant; Flags: Integer; CodePage: Integer); dispid 7;
procedure Save(const pVar: OleVariant; Flags: Integer; CodePage: Integer); dispid 8;
function Freeze: Integer; dispid 9;
function Unfreeze: Integer; dispid 10;
procedure BeginEditCollection; dispid 11;
procedure EndEditCollection; dispid 12;
function Undo(Count: Integer): Integer; dispid 13;
function Redo(Count: Integer): Integer; dispid 14;
function Range(cp1: Integer; cp2: Integer): ITextRange; dispid 15;
function RangeFromPoint(x: Integer; y: Integer): ITextRange; dispid 16;
end;
// *********************************************************************//
// Interface: ITextRange
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {8CC497C2-A1DF-11CE-8098-00AA0047BE5D}
// *********************************************************************//
ITextRange = interface(IDispatch)
['{8CC497C2-A1DF-11CE-8098-00AA0047BE5D}']
function Get_Text: WideString; safecall;
procedure Set_Text(const pbstr: WideString); safecall;
function Get_Char: Integer; safecall;
procedure Set_Char(pch: Integer); safecall;
function Get_Duplicate: ITextRange; safecall;
function Get_FormattedText: ITextRange; safecall;
procedure Set_FormattedText(const ppRange: ITextRange); safecall;
function Get_Start: Integer; safecall;
procedure Set_Start(pcpFirst: Integer); safecall;
function Get_End_: Integer; safecall;
procedure Set_End_(pcpLim: Integer); safecall;
function Get_Font: ITextFont; safecall;
procedure Set_Font(const pFont: ITextFont); safecall;
function Get_Para: ITextPara; safecall;
procedure Set_Para(const pPara: ITextPara); safecall;
function Get_StoryLength: Integer; safecall;
function Get_StoryType: Integer; safecall;
procedure Collapse(bStart: Integer); safecall;
function Expand(Unit_: Integer): Integer; safecall;
function GetIndex(Unit_: Integer): Integer; safecall;
procedure SetIndex(Unit_: Integer; Index: Integer; Extend: Integer); safecall;
procedure SetRange(cpActive: Integer; cpOther: Integer); safecall;
function InRange(const pRange: ITextRange): Integer; safecall;
function InStory(const pRange: ITextRange): Integer; safecall;
function IsEqual(const pRange: ITextRange): Integer; safecall;
procedure Select; safecall;
function StartOf(Unit_: Integer; Extend: Integer): Integer; safecall;
function EndOf(Unit_: Integer; Extend: Integer): Integer; safecall;
function Move(Unit_: Integer; Count: Integer): Integer; safecall;
function MoveStart(Unit_: Integer; Count: Integer): Integer; safecall;
function MoveEnd(Unit_: Integer; Count: Integer): Integer; safecall;
function MoveWhile(const Cset: OleVariant; Count: Integer): Integer; safecall;
function MoveStartWhile(const Cset: OleVariant; Count: Integer): Integer; safecall;
function MoveEndWhile(const Cset: OleVariant; Count: Integer): Integer; safecall;
function MoveUntil(const Cset: OleVariant; Count: Integer): Integer; safecall;
function MoveStartUntil(const Cset: OleVariant; Count: Integer): Integer; safecall;
function MoveEndUntil(const Cset: OleVariant; Count: Integer): Integer; safecall;
function FindText(const bstr: WideString; cch: Integer; Flags: Integer): Integer; safecall;
function FindTextStart(const bstr: WideString; cch: Integer; Flags: Integer): Integer; safecall;
function FindTextEnd(const bstr: WideString; cch: Integer; Flags: Integer): Integer; safecall;
function Delete(Unit_: Integer; Count: Integer): Integer; safecall;
procedure Cut(out pVar: OleVariant); safecall;
procedure Copy(out pVar: OleVariant); safecall;
procedure Paste(const pVar: OleVariant; Format: Integer); safecall;
function CanPaste(const pVar: OleVariant; Format: Integer): Integer; safecall;
function CanEdit: Integer; safecall;
procedure ChangeCase(Type_: Integer); safecall;
procedure GetPoint(Type_: Integer; out px: Integer; out py: Integer); safecall;
procedure SetPoint(x: Integer; y: Integer; Type_: Integer; Extend: Integer); safecall;
procedure ScrollIntoView(Value: Integer); safecall;
function GetEmbeddedObject: IUnknown; safecall;
property Text: WideString read Get_Text write Set_Text;
property Char: Integer read Get_Char write Set_Char;
property Duplicate: ITextRange read Get_Duplicate;
property FormattedText: ITextRange read Get_FormattedText write Set_FormattedText;
property Start: Integer read Get_Start write Set_Start;
property End_: Integer read Get_End_ write Set_End_;
property Font: ITextFont read Get_Font write Set_Font;
property Para: ITextPara read Get_Para write Set_Para;
property StoryLength: Integer read Get_StoryLength;
property StoryType: Integer read Get_StoryType;
end;
// *********************************************************************//
// DispIntf: ITextRangeDisp
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {8CC497C2-A1DF-11CE-8098-00AA0047BE5D}
// *********************************************************************//
ITextRangeDisp = dispinterface
['{8CC497C2-A1DF-11CE-8098-00AA0047BE5D}']
property Text: WideString dispid 0;
property Char: Integer dispid 513;
property Duplicate: ITextRange readonly dispid 514;
property FormattedText: ITextRange dispid 515;
property Start: Integer dispid 516;
property End_: Integer dispid 517;
property Font: ITextFont dispid 518;
property Para: ITextPara dispid 519;
property StoryLength: Integer readonly dispid 520;
property StoryType: Integer readonly dispid 521;
procedure Collapse(bStart: Integer); dispid 528;
function Expand(Unit_: Integer): Integer; dispid 529;
function GetIndex(Unit_: Integer): Integer; dispid 530;
procedure SetIndex(Unit_: Integer; Index: Integer; Extend: Integer); dispid 531;
procedure SetRange(cpActive: Integer; cpOther: Integer); dispid 532;
function InRange(const pRange: ITextRange): Integer; dispid 533;
function InStory(const pRange: ITextRange): Integer; dispid 534;
function IsEqual(const pRange: ITextRange): Integer; dispid 535;
procedure Select; dispid 536;
function StartOf(Unit_: Integer; Extend: Integer): Integer; dispid 537;
function EndOf(Unit_: Integer; Extend: Integer): Integer; dispid 544;
function Move(Unit_: Integer; Count: Integer): Integer; dispid 545;
function MoveStart(Unit_: Integer; Count: Integer): Integer; dispid 546;
function MoveEnd(Unit_: Integer; Count: Integer): Integer; dispid 547;
function MoveWhile(const Cset: OleVariant; Count: Integer): Integer; dispid 548;
function MoveStartWhile(const Cset: OleVariant; Count: Integer): Integer; dispid 549;
function MoveEndWhile(const Cset: OleVariant; Count: Integer): Integer; dispid 550;
function MoveUntil(const Cset: OleVariant; Count: Integer): Integer; dispid 551;
function MoveStartUntil(const Cset: OleVariant; Count: Integer): Integer; dispid 552;
function MoveEndUntil(const Cset: OleVariant; Count: Integer): Integer; dispid 553;
function FindText(const bstr: WideString; cch: Integer; Flags: Integer): Integer; dispid 560;
function FindTextStart(const bstr: WideString; cch: Integer; Flags: Integer): Integer; dispid 561;
function FindTextEnd(const bstr: WideString; cch: Integer; Flags: Integer): Integer; dispid 562;
function Delete(Unit_: Integer; Count: Integer): Integer; dispid 563;
procedure Cut(out pVar: OleVariant); dispid 564;
procedure Copy(out pVar: OleVariant); dispid 565;
procedure Paste(const pVar: OleVariant; Format: Integer); dispid 566;
function CanPaste(const pVar: OleVariant; Format: Integer): Integer; dispid 567;
function CanEdit: Integer; dispid 568;
procedure ChangeCase(Type_: Integer); dispid 569;
procedure GetPoint(Type_: Integer; out px: Integer; out py: Integer); dispid 576;
procedure SetPoint(x: Integer; y: Integer; Type_: Integer; Extend: Integer); dispid 577;
procedure ScrollIntoView(Value: Integer); dispid 578;
function GetEmbeddedObject: IUnknown; dispid 579;
end;
// *********************************************************************//
// Interface: ITextSelection
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {8CC497C1-A1DF-11CE-8098-00AA0047BE5D}
// *********************************************************************//
ITextSelection = interface(ITextRange)
['{8CC497C1-A1DF-11CE-8098-00AA0047BE5D}']
function Get_Flags: Integer; safecall;
procedure Set_Flags(pFlags: Integer); safecall;
function Get_type_: Integer; safecall;
function MoveLeft(Unit_: Integer; Count: Integer; Extend: Integer): Integer; safecall;
function MoveRight(Unit_: Integer; Count: Integer; Extend: Integer): Integer; safecall;
function MoveUp(Unit_: Integer; Count: Integer; Extend: Integer): Integer; safecall;
function MoveDown(Unit_: Integer; Count: Integer; Extend: Integer): Integer; safecall;
function HomeKey(Unit_: Integer; Extend: Integer): Integer; safecall;
function EndKey(Unit_: Integer; Extend: Integer): Integer; safecall;
procedure TypeText(const bstr: WideString); safecall;
property Flags: Integer read Get_Flags write Set_Flags;
property type_: Integer read Get_type_;
end;
// *********************************************************************//
// DispIntf: ITextSelectionDisp
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {8CC497C1-A1DF-11CE-8098-00AA0047BE5D}
// *********************************************************************//
ITextSelectionDisp = dispinterface
['{8CC497C1-A1DF-11CE-8098-00AA0047BE5D}']
property Flags: Integer dispid 257;
property type_: Integer readonly dispid 258;
function MoveLeft(Unit_: Integer; Count: Integer; Extend: Integer): Integer; dispid 259;
function MoveRight(Unit_: Integer; Count: Integer; Extend: Integer): Integer; dispid 260;
function MoveUp(Unit_: Integer; Count: Integer; Extend: Integer): Integer; dispid 261;
function MoveDown(Unit_: Integer; Count: Integer; Extend: Integer): Integer; dispid 262;
function HomeKey(Unit_: Integer; Extend: Integer): Integer; dispid 263;
function EndKey(Unit_: Integer; Extend: Integer): Integer; dispid 264;
procedure TypeText(const bstr: WideString); dispid 265;
property Text: WideString dispid 0;
property Char: Integer dispid 513;
property Duplicate: ITextRange readonly dispid 514;
property FormattedText: ITextRange dispid 515;
property Start: Integer dispid 516;
property End_: Integer dispid 517;
property Font: ITextFont dispid 518;
property Para: ITextPara dispid 519;
property StoryLength: Integer readonly dispid 520;
property StoryType: Integer readonly dispid 521;
procedure Collapse(bStart: Integer); dispid 528;
function Expand(Unit_: Integer): Integer; dispid 529;
function GetIndex(Unit_: Integer): Integer; dispid 530;
procedure SetIndex(Unit_: Integer; Index: Integer; Extend: Integer); dispid 531;
procedure SetRange(cpActive: Integer; cpOther: Integer); dispid 532;
function InRange(const pRange: ITextRange): Integer; dispid 533;
function InStory(const pRange: ITextRange): Integer; dispid 534;
function IsEqual(const pRange: ITextRange): Integer; dispid 535;
procedure Select; dispid 536;
function StartOf(Unit_: Integer; Extend: Integer): Integer; dispid 537;
function EndOf(Unit_: Integer; Extend: Integer): Integer; dispid 544;
function Move(Unit_: Integer; Count: Integer): Integer; dispid 545;
function MoveStart(Unit_: Integer; Count: Integer): Integer; dispid 546;
function MoveEnd(Unit_: Integer; Count: Integer): Integer; dispid 547;
function MoveWhile(const Cset: OleVariant; Count: Integer): Integer; dispid 548;
function MoveStartWhile(const Cset: OleVariant; Count: Integer): Integer; dispid 549;
function MoveEndWhile(const Cset: OleVariant; Count: Integer): Integer; dispid 550;
function MoveUntil(const Cset: OleVariant; Count: Integer): Integer; dispid 551;
function MoveStartUntil(const Cset: OleVariant; Count: Integer): Integer; dispid 552;
function MoveEndUntil(const Cset: OleVariant; Count: Integer): Integer; dispid 553;
function FindText(const bstr: WideString; cch: Integer; Flags: Integer): Integer; dispid 560;
function FindTextStart(const bstr: WideString; cch: Integer; Flags: Integer): Integer; dispid 561;
function FindTextEnd(const bstr: WideString; cch: Integer; Flags: Integer): Integer; dispid 562;
function Delete(Unit_: Integer; Count: Integer): Integer; dispid 563;
procedure Cut(out pVar: OleVariant); dispid 564;
procedure Copy(out pVar: OleVariant); dispid 565;
procedure Paste(const pVar: OleVariant; Format: Integer); dispid 566;
function CanPaste(const pVar: OleVariant; Format: Integer): Integer; dispid 567;
function CanEdit: Integer; dispid 568;
procedure ChangeCase(Type_: Integer); dispid 569;
procedure GetPoint(Type_: Integer; out px: Integer; out py: Integer); dispid 576;
procedure SetPoint(x: Integer; y: Integer; Type_: Integer; Extend: Integer); dispid 577;
procedure ScrollIntoView(Value: Integer); dispid 578;
function GetEmbeddedObject: IUnknown; dispid 579;
end;
// *********************************************************************//
// Interface: ITextFont
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {8CC497C3-A1DF-11CE-8098-00AA0047BE5D}
// *********************************************************************//
ITextFont = interface(IDispatch)
['{8CC497C3-A1DF-11CE-8098-00AA0047BE5D}']
function Get_Duplicate: ITextFont; safecall;
procedure Set_Duplicate(const ppFont: ITextFont); safecall;
function CanChange: Integer; safecall;
function IsEqual(const pFont: ITextFont): Integer; safecall;
procedure Reset(Value: Integer); safecall;
function Get_Style: Integer; safecall;
procedure Set_Style(pValue: Integer); safecall;
function Get_AllCaps: Integer; safecall;
procedure Set_AllCaps(pValue: Integer); safecall;
function Get_Animation: Integer; safecall;
procedure Set_Animation(pValue: Integer); safecall;
function Get_BackColor: Integer; safecall;
procedure Set_BackColor(pValue: Integer); safecall;
function Get_Bold: Integer; safecall;
procedure Set_Bold(pValue: Integer); safecall;
function Get_Emboss: Integer; safecall;
procedure Set_Emboss(pValue: Integer); safecall;
function Get_ForeColor: Integer; safecall;
procedure Set_ForeColor(pValue: Integer); safecall;
function Get_Hidden: Integer; safecall;
procedure Set_Hidden(pValue: Integer); safecall;
function Get_Engrave: Integer; safecall;
procedure Set_Engrave(pValue: Integer); safecall;
function Get_Italic: Integer; safecall;
procedure Set_Italic(pValue: Integer); safecall;
function Get_Kerning: Single; safecall;
procedure Set_Kerning(pValue: Single); safecall;
function Get_LanguageID: Integer; safecall;
procedure Set_LanguageID(pValue: Integer); safecall;
function Get_Name: WideString; safecall;
procedure Set_Name(const pbstr: WideString); safecall;
function Get_Outline: Integer; safecall;
procedure Set_Outline(pValue: Integer); safecall;
function Get_Position: Single; safecall;
procedure Set_Position(pValue: Single); safecall;
function Get_Protected_: Integer; safecall;
procedure Set_Protected_(pValue: Integer); safecall;
function Get_Shadow: Integer; safecall;
procedure Set_Shadow(pValue: Integer); safecall;
function Get_Size: Single; safecall;
procedure Set_Size(pValue: Single); safecall;
function Get_SmallCaps: Integer; safecall;
procedure Set_SmallCaps(pValue: Integer); safecall;
function Get_Spacing: Single; safecall;
procedure Set_Spacing(pValue: Single); safecall;
function Get_StrikeThrough: Integer; safecall;
procedure Set_StrikeThrough(pValue: Integer); safecall;
function Get_Subscript: Integer; safecall;
procedure Set_Subscript(pValue: Integer); safecall;
function Get_Superscript: Integer; safecall;
procedure Set_Superscript(pValue: Integer); safecall;
function Get_Underline: Integer; safecall;
procedure Set_Underline(pValue: Integer); safecall;
function Get_Weight: Integer; safecall;
procedure Set_Weight(pValue: Integer); safecall;
property Duplicate: ITextFont read Get_Duplicate write Set_Duplicate;
property Style: Integer read Get_Style write Set_Style;
property AllCaps: Integer read Get_AllCaps write Set_AllCaps;
property Animation: Integer read Get_Animation write Set_Animation;
property BackColor: Integer read Get_BackColor write Set_BackColor;
property Bold: Integer read Get_Bold write Set_Bold;
property Emboss: Integer read Get_Emboss write Set_Emboss;
property ForeColor: Integer read Get_ForeColor write Set_ForeColor;
property Hidden: Integer read Get_Hidden write Set_Hidden;
property Engrave: Integer read Get_Engrave write Set_Engrave;
property Italic: Integer read Get_Italic write Set_Italic;
property Kerning: Single read Get_Kerning write Set_Kerning;
property LanguageID: Integer read Get_LanguageID write Set_LanguageID;
property Name: WideString read Get_Name write Set_Name;
property Outline: Integer read Get_Outline write Set_Outline;
property Position: Single read Get_Position write Set_Position;
property Protected_: Integer read Get_Protected_ write Set_Protected_;
property Shadow: Integer read Get_Shadow write Set_Shadow;
property Size: Single read Get_Size write Set_Size;
property SmallCaps: Integer read Get_SmallCaps write Set_SmallCaps;
property Spacing: Single read Get_Spacing write Set_Spacing;
property StrikeThrough: Integer read Get_StrikeThrough write Set_StrikeThrough;
property Subscript: Integer read Get_Subscript write Set_Subscript;
property Superscript: Integer read Get_Superscript write Set_Superscript;
property Underline: Integer read Get_Underline write Set_Underline;
property Weight: Integer read Get_Weight write Set_Weight;
end;
// *********************************************************************//
// DispIntf: ITextFontDisp
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {8CC497C3-A1DF-11CE-8098-00AA0047BE5D}
// *********************************************************************//
ITextFontDisp = dispinterface
['{8CC497C3-A1DF-11CE-8098-00AA0047BE5D}']
property Duplicate: ITextFont dispid 0;
function CanChange: Integer; dispid 769;
function IsEqual(const pFont: ITextFont): Integer; dispid 770;
procedure Reset(Value: Integer); dispid 771;
property Style: Integer dispid 772;
property AllCaps: Integer dispid 773;
property Animation: Integer dispid 774;
property BackColor: Integer dispid 775;
property Bold: Integer dispid 776;
property Emboss: Integer dispid 777;
property ForeColor: Integer dispid 784;
property Hidden: Integer dispid 785;
property Engrave: Integer dispid 786;
property Italic: Integer dispid 787;
property Kerning: Single dispid 788;
property LanguageID: Integer dispid 789;
property Name: WideString dispid 790;
property Outline: Integer dispid 791;
property Position: Single dispid 792;
property Protected_: Integer dispid 793;
property Shadow: Integer dispid 800;
property Size: Single dispid 801;
property SmallCaps: Integer dispid 802;
property Spacing: Single dispid 803;
property StrikeThrough: Integer dispid 804;
property Subscript: Integer dispid 805;
property Superscript: Integer dispid 806;
property Underline: Integer dispid 807;
property Weight: Integer dispid 808;
end;
// *********************************************************************//
// Interface: ITextPara
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {8CC497C4-A1DF-11CE-8098-00AA0047BE5D}
// *********************************************************************//
ITextPara = interface(IDispatch)
['{8CC497C4-A1DF-11CE-8098-00AA0047BE5D}']
function Get_Duplicate: ITextPara; safecall;
procedure Set_Duplicate(const ppPara: ITextPara); safecall;
function CanChange: Integer; safecall;
function IsEqual(const pPara: ITextPara): Integer; safecall;
procedure Reset(Value: Integer); safecall;
function Get_Style: Integer; safecall;
procedure Set_Style(pValue: Integer); safecall;
function Get_Alignment: Integer; safecall;
procedure Set_Alignment(pValue: Integer); safecall;
function Get_Hyphenation: Integer; safecall;
procedure Set_Hyphenation(pValue: Integer); safecall;
function Get_FirstLineIndent: Single; safecall;
function Get_KeepTogether: Integer; safecall;
procedure Set_KeepTogether(pValue: Integer); safecall;
function Get_KeepWithNext: Integer; safecall;
procedure Set_KeepWithNext(pValue: Integer); safecall;
function Get_LeftIndent: Single; safecall;
function Get_LineSpacing: Single; safecall;
function Get_LineSpacingRule: Integer; safecall;
function Get_ListAlignment: Integer; safecall;
procedure Set_ListAlignment(pValue: Integer); safecall;
function Get_ListLevelIndex: Integer; safecall;
procedure Set_ListLevelIndex(pValue: Integer); safecall;
function Get_ListStart: Integer; safecall;
procedure Set_ListStart(pValue: Integer); safecall;
function Get_ListTab: Single; safecall;
procedure Set_ListTab(pValue: Single); safecall;
function Get_ListType: Integer; safecall;
procedure Set_ListType(pValue: Integer); safecall;
function Get_NoLineNumber: Integer; safecall;
procedure Set_NoLineNumber(pValue: Integer); safecall;
function Get_PageBreakBefore: Integer; safecall;
procedure Set_PageBreakBefore(pValue: Integer); safecall;
function Get_RightIndent: Single; safecall;
procedure Set_RightIndent(pValue: Single); safecall;
procedure SetIndents(StartIndent: Single; LeftIndent: Single; RightIndent: Single); safecall;
procedure SetLineSpacing(LineSpacingRule: Integer; LineSpacing: Single); safecall;
function Get_SpaceAfter: Single; safecall;
procedure Set_SpaceAfter(pValue: Single); safecall;
function Get_SpaceBefore: Single; safecall;
procedure Set_SpaceBefore(pValue: Single); safecall;
function Get_WidowControl: Integer; safecall;
procedure Set_WidowControl(pValue: Integer); safecall;
function Get_TabCount: Integer; safecall;
procedure AddTab(tbPos: Single; tbAlign: Integer; tbLeader: Integer); safecall;
procedure ClearAllTabs; safecall;
procedure DeleteTab(tbPos: Single); safecall;
procedure GetTab(iTab: Integer; out ptbPos: Single; out ptbAlign: Integer;
out ptbLeader: Integer); safecall;
property Duplicate: ITextPara read Get_Duplicate write Set_Duplicate;
property Style: Integer read Get_Style write Set_Style;
property Alignment: Integer read Get_Alignment write Set_Alignment;
property Hyphenation: Integer read Get_Hyphenation write Set_Hyphenation;
property FirstLineIndent: Single read Get_FirstLineIndent;
property KeepTogether: Integer read Get_KeepTogether write Set_KeepTogether;
property KeepWithNext: Integer read Get_KeepWithNext write Set_KeepWithNext;
property LeftIndent: Single read Get_LeftIndent;
property LineSpacing: Single read Get_LineSpacing;
property LineSpacingRule: Integer read Get_LineSpacingRule;
property ListAlignment: Integer read Get_ListAlignment write Set_ListAlignment;
property ListLevelIndex: Integer read Get_ListLevelIndex write Set_ListLevelIndex;
property ListStart: Integer read Get_ListStart write Set_ListStart;
property ListTab: Single read Get_ListTab write Set_ListTab;
property ListType: Integer read Get_ListType write Set_ListType;
property NoLineNumber: Integer read Get_NoLineNumber write Set_NoLineNumber;
property PageBreakBefore: Integer read Get_PageBreakBefore write Set_PageBreakBefore;
property RightIndent: Single read Get_RightIndent write Set_RightIndent;
property SpaceAfter: Single read Get_SpaceAfter write Set_SpaceAfter;
property SpaceBefore: Single read Get_SpaceBefore write Set_SpaceBefore;
property WidowControl: Integer read Get_WidowControl write Set_WidowControl;
property TabCount: Integer read Get_TabCount;
end;
// *********************************************************************//
// DispIntf: ITextParaDisp
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {8CC497C4-A1DF-11CE-8098-00AA0047BE5D}
// *********************************************************************//
ITextParaDisp = dispinterface
['{8CC497C4-A1DF-11CE-8098-00AA0047BE5D}']
property Duplicate: ITextPara dispid 0;
function CanChange: Integer; dispid 1025;
function IsEqual(const pPara: ITextPara): Integer; dispid 1026;
procedure Reset(Value: Integer); dispid 1027;
property Style: Integer dispid 1028;
property Alignment: Integer dispid 1029;
property Hyphenation: Integer dispid 1030;
property FirstLineIndent: Single readonly dispid 1031;
property KeepTogether: Integer dispid 1032;
property KeepWithNext: Integer dispid 1033;
property LeftIndent: Single readonly dispid 1040;
property LineSpacing: Single readonly dispid 1041;
property LineSpacingRule: Integer readonly dispid 1042;
property ListAlignment: Integer dispid 1043;
property ListLevelIndex: Integer dispid 1044;
property ListStart: Integer dispid 1045;
property ListTab: Single dispid 1046;
property ListType: Integer dispid 1047;
property NoLineNumber: Integer dispid 1048;
property PageBreakBefore: Integer dispid 1049;
property RightIndent: Single dispid 1056;
procedure SetIndents(StartIndent: Single; LeftIndent: Single; RightIndent: Single); dispid 1057;
procedure SetLineSpacing(LineSpacingRule: Integer; LineSpacing: Single); dispid 1058;
property SpaceAfter: Single dispid 1059;
property SpaceBefore: Single dispid 1060;
property WidowControl: Integer dispid 1061;
property TabCount: Integer readonly dispid 1062;
procedure AddTab(tbPos: Single; tbAlign: Integer; tbLeader: Integer); dispid 1063;
procedure ClearAllTabs; dispid 1064;
procedure DeleteTab(tbPos: Single); dispid 1065;
procedure GetTab(iTab: Integer; out ptbPos: Single; out ptbAlign: Integer;
out ptbLeader: Integer); dispid 1072;
end;
// *********************************************************************//
// Interface: ITextStoryRanges
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {8CC497C5-A1DF-11CE-8098-00AA0047BE5D}
// *********************************************************************//
ITextStoryRanges = interface(IDispatch)
['{8CC497C5-A1DF-11CE-8098-00AA0047BE5D}']
function _NewEnum: IUnknown; safecall;
function Item(Index: Integer): ITextRange; safecall;
function Get_Count: Integer; safecall;
property Count: Integer read Get_Count;
end;
// *********************************************************************//
// DispIntf: ITextStoryRangesDisp
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {8CC497C5-A1DF-11CE-8098-00AA0047BE5D}
// *********************************************************************//
ITextStoryRangesDisp = dispinterface
['{8CC497C5-A1DF-11CE-8098-00AA0047BE5D}']
function _NewEnum: IUnknown; dispid -4;
function Item(Index: Integer): ITextRange; dispid 0;
property Count: Integer readonly dispid 2;
end;
// *********************************************************************//
// Interface: ITextDocument2
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {01C25500-4268-11D1-883A-3C8B00C10000}
// *********************************************************************//
ITextDocument2 = interface(ITextDocument)
['{01C25500-4268-11D1-883A-3C8B00C10000}']
procedure AttachMsgFilter(const pFilter: IUnknown); safecall;
procedure SetEffectColor(Index: Integer; cr: LongWord); safecall;
procedure GetEffectColor(Index: Integer; out pcr: LongWord); safecall;
function Get_CaretType: Integer; safecall;
procedure Set_CaretType(pCaretType: Integer); safecall;
function GetImmContext: Integer; safecall;
procedure ReleaseImmContext(Context: Integer); safecall;
procedure GetPreferredFont(cp: Integer; CodePage: Integer; Option: Integer;
curCodepage: Integer; curFontSize: Integer; out pbstr: WideString;
out pPitchAndFamily: Integer; out pNewFontSize: Integer); safecall;
function Get_NotificationMode: Integer; safecall;
procedure Set_NotificationMode(pMode: Integer); safecall;
procedure GetClientRect(Type_: Integer; out pLeft: Integer; out pTop: Integer;
out pRight: Integer; out pBottom: Integer); safecall;
function Get_SelectionEx: ITextSelection; safecall;
procedure GetWindow(out phWnd: Integer); safecall;
procedure GetFEFlags(out pFlags: Integer); safecall;
procedure UpdateWindow; safecall;
procedure CheckTextLimit(cch: Integer; var pcch: Integer); safecall;
procedure IMEInProgress(Mode: Integer); safecall;
procedure SysBeep; safecall;
procedure Update(Mode: Integer); safecall;
procedure Notify(Notify: Integer); safecall;
property CaretType: Integer read Get_CaretType write Set_CaretType;
property NotificationMode: Integer read Get_NotificationMode write Set_NotificationMode;
property SelectionEx: ITextSelection read Get_SelectionEx;
end;
// *********************************************************************//
// DispIntf: ITextDocument2Disp
// Flags: (4544) Dual NonExtensible OleAutomation Dispatchable
// GUID: {01C25500-4268-11D1-883A-3C8B00C10000}
// *********************************************************************//
ITextDocument2Disp = dispinterface
['{01C25500-4268-11D1-883A-3C8B00C10000}']
procedure AttachMsgFilter(const pFilter: IUnknown); dispid 21;
procedure SetEffectColor(Index: Integer; cr: LongWord); dispid 22;
procedure GetEffectColor(Index: Integer; out pcr: LongWord); dispid 23;
property CaretType: Integer dispid 24;
function GetImmContext: Integer; dispid 25;
procedure ReleaseImmContext(Context: Integer); dispid 26;
procedure GetPreferredFont(cp: Integer; CodePage: Integer; Option: Integer;
curCodepage: Integer; curFontSize: Integer; out pbstr: WideString;
out pPitchAndFamily: Integer; out pNewFontSize: Integer); dispid 27;
property NotificationMode: Integer dispid 28;
procedure GetClientRect(Type_: Integer; out pLeft: Integer; out pTop: Integer;
out pRight: Integer; out pBottom: Integer); dispid 29;
property SelectionEx: ITextSelection readonly dispid 30;
procedure GetWindow(out phWnd: Integer); dispid 31;
procedure GetFEFlags(out pFlags: Integer); dispid 32;
procedure UpdateWindow; dispid 33;
procedure CheckTextLimit(cch: Integer; var pcch: Integer); dispid 34;
procedure IMEInProgress(Mode: Integer); dispid 35;
procedure SysBeep; dispid 36;
procedure Update(Mode: Integer); dispid 37;
procedure Notify(Notify: Integer); dispid 38;
property Name: WideString readonly dispid 0;
property Selection: ITextSelection readonly dispid 1;
property StoryCount: Integer readonly dispid 2;
property StoryRanges: ITextStoryRanges readonly dispid 3;
property Saved: Integer dispid 4;
property DefaultTabStop: Single dispid 5;
procedure New; dispid 6;
procedure Open(const pVar: OleVariant; Flags: Integer; CodePage: Integer); dispid 7;
procedure Save(const pVar: OleVariant; Flags: Integer; CodePage: Integer); dispid 8;
function Freeze: Integer; dispid 9;
function Unfreeze: Integer; dispid 10;
procedure BeginEditCollection; dispid 11;
procedure EndEditCollection; dispid 12;
function Undo(Count: Integer): Integer; dispid 13;
function Redo(Count: Integer): Integer; dispid 14;
function Range(cp1: Integer; cp2: Integer): ITextRange; dispid 15;
function RangeFromPoint(x: Integer; y: Integer): ITextRange; dispid 16;
end;
// *********************************************************************//
// Interface: ITextMsgFilter
// Flags: (128) NonExtensible
// GUID: {A3787420-4267-11D1-883A-3C8B00C10000}
// *********************************************************************//
ITextMsgFilter = interface(IUnknown)
['{A3787420-4267-11D1-883A-3C8B00C10000}']
function AttachDocument(var hwnd: _RemotableHandle; const pTextDoc: ITextDocument2): HResult; stdcall;
function HandleMessage(var pmsg: SYSUINT; var pwparam: UINT_PTR; var plparam: LONG_PTR;
out plres: LONG_PTR): HResult; stdcall;
function AttachMsgFilter(const pMsgFilter: ITextMsgFilter): HResult; stdcall;
end;
implementation
uses System.Win.ComObj;
end.

View file

@ -5,6 +5,7 @@ import { CompilerEvent, CompilerCallbacks, CompilerPathCallbacks, CompilerFileSy
CompilerFileSystemAsyncCallbacks,
CompilerErrorSeverity} from '@keymanapp/developer-utils';
import { fileURLToPath } from 'url';
import { Suite } from 'mocha';
const { TEST_SAVE_FIXTURES } = process.env;
@ -12,16 +13,27 @@ const { TEST_SAVE_FIXTURES } = process.env;
* A CompilerCallbacks implementation for testing
*/
export class TestCompilerCallbacks implements CompilerCallbacks {
/* TestCompilerCallbacks */
private readonly suite: Suite;
messages: CompilerEvent[] = [];
readonly _net: TestCompilerNetAsyncCallbacks;
readonly _fsAsync: DefaultCompilerFileSystemAsyncCallbacks = new DefaultCompilerFileSystemAsyncCallbacks(this);
constructor(basePath?: string) {
constructor(suite?: Suite, basePath?: string) {
if(basePath) {
this._net = new TestCompilerNetAsyncCallbacks(basePath);
}
this.suite = suite;
if(this.suite) {
const _this = this;
this.suite.beforeEach(function() {
_this.clear();
});
this.suite.afterEach(function() {
if(this.currentTest?.isFailed()) {
_this.printMessages();
}
})
}
}
clear() {
@ -55,7 +67,7 @@ export class TestCompilerCallbacks implements CompilerCallbacks {
loadFile(filename: string): Uint8Array {
try {
return fs.readFileSync(filename);
return fs.readFileSync(filename) as Uint8Array;
} catch(e) {
if (e.code === 'ENOENT') {
return null;
@ -162,7 +174,7 @@ class TestCompilerNetAsyncCallbacks implements CompilerNetAsyncCallbacks {
// missing file, this is okay
return null;
}
const data: Uint8Array = fs.readFileSync(p);
const data = fs.readFileSync(p) as Uint8Array;
return data;
}

View file

@ -11,7 +11,7 @@
"dependencies": {
"@keymanapp/common-types": "*",
"eventemitter3": "^5.0.0",
"fast-xml-parser": "^5.3.4",
"fast-xml-parser": "^5.3.8",
"path-browserify": "^1.0.1",
"restructure": "^3.0.1",
"sax": ">=0.6.0",

View file

@ -79,4 +79,10 @@ export class DeveloperUtilsMessages {
this.ERROR_ProjectFileCouldNotBeRead,
`Project file could not be read`
);
static ERROR_NotAPackageFile = SevError | 0x000D;
static Error_NotAPackageFile = () => m(
this.ERROR_NotAPackageFile,
`Package source file is not a valid .kps file because it is missing the <Package> root element.`,
);
};

View file

@ -22,19 +22,17 @@ export class KpsFileReader {
public read(file: Uint8Array): KpsPackage {
const data = new TextDecoder().decode(file);
const kpsPackage = (() => {
let a: KpsPackage;
let kpsPackage: KpsPackage = null;
try {
kpsPackage = new KeymanXMLReader('kps')
.parse(data) as KpsPackage;
} catch(e) {
this.callbacks.reportMessage(DeveloperUtilsMessages.Error_InvalidPackageFile({e}));
return null;
}
try {
a = new KeymanXMLReader('kps')
.parse(data) as KpsPackage;
} catch(e) {
this.callbacks.reportMessage(DeveloperUtilsMessages.Error_InvalidPackageFile({e}));
}
return a;
})();
if(!kpsPackage) {
if(!kpsPackage?.Package) {
this.callbacks.reportMessage(DeveloperUtilsMessages.Error_NotAPackageFile());
return null;
}
@ -42,12 +40,12 @@ export class KpsFileReader {
}
private boxArrays(data: KpsPackage): KpsPackage {
boxXmlArray(data.Package?.Files, 'File');
boxXmlArray(data.Package?.Keyboards, 'Keyboard');
boxXmlArray(data.Package?.LexicalModels, 'LexicalModel');
boxXmlArray(data.Package?.RelatedPackages, 'RelatedPackage');
boxXmlArray(data.Package.Files, 'File');
boxXmlArray(data.Package.Keyboards, 'Keyboard');
boxXmlArray(data.Package.LexicalModels, 'LexicalModel');
boxXmlArray(data.Package.RelatedPackages, 'RelatedPackage');
if(data.Package?.Keyboards?.Keyboard) {
if(data.Package.Keyboards?.Keyboard) {
for(const k of data.Package.Keyboards.Keyboard) {
boxXmlArray(k.Examples, 'Example');
boxXmlArray(k.Languages, 'Language');
@ -56,17 +54,17 @@ export class KpsFileReader {
}
}
if(data.Package?.LexicalModels?.LexicalModel) {
if(data.Package.LexicalModels?.LexicalModel) {
for(const lm of data.Package.LexicalModels.LexicalModel) {
boxXmlArray(lm.Languages, 'Language');
}
}
boxXmlArray(data.Package?.RelatedPackages, 'RelatedPackage');
boxXmlArray(data.Package?.StartMenu?.Items, 'Item');
boxXmlArray(data.Package?.Strings, 'String');
boxXmlArray(data.Package.RelatedPackages, 'RelatedPackage');
boxXmlArray(data.Package.StartMenu?.Items, 'Item');
boxXmlArray(data.Package.Strings, 'String');
if(data.Package?.Info) {
if(data.Package.Info) {
// If no properties are defined on an <Info> subitem, then it will be a
// string, and needs to be boxed into a KpsInfoItem object
const info: any = data.Package.Info;

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<NotAPackage>
<!-- root element should be <Package> -->
</NotAPackage>

View file

@ -6,13 +6,15 @@ import { KPJFileReader } from "../../src/types/kpj/kpj-file-reader.js";
import { KeymanDeveloperProjectFile10, KeymanDeveloperProjectType } from '../../src/types/kpj/keyman-developer-project.js';
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
const callbacks = new TestCompilerCallbacks();
describe('kpj-file-reader', function () {
const callbacks = new TestCompilerCallbacks(this);
it('kpj-file-reader should read a valid file', async function() {
const kpjPath = 'khmer_angkor.kpj';
const path = makePathToFixture('kpj', kpjPath);
const input = fs.readFileSync(path);
const input = fs.readFileSync(path) as Uint8Array;
const reader = new KPJFileReader(callbacks);
const kpj = reader.read(input);
reader.validate(kpj);
@ -124,7 +126,7 @@ describe('kpj-file-reader', function () {
it('should load a v1.0 keyboard project with missing <File>', async function() {
const path = makePathToFixture('kpj', 'project-missing-file', 'project_missing_file.kpj');
const input = fs.readFileSync(path);
const input = fs.readFileSync(path) as Uint8Array;
const reader = new KPJFileReader(callbacks);
const kpj = reader.read(input);
reader.validate(kpj);
@ -140,7 +142,7 @@ describe('kpj-file-reader', function () {
it('should load a v1.0 keyboard project with missing <Files>', async function() {
const path = makePathToFixture('kpj', 'project-missing-file', 'project_missing_files.kpj');
const input = fs.readFileSync(path);
const input = fs.readFileSync(path) as Uint8Array;
const reader = new KPJFileReader(callbacks);
const kpj = reader.read(input);
reader.validate(kpj);

View file

@ -1,4 +1,7 @@
import * as fs from 'fs';
/*
* Keyman is copyright (C) SIL Global. MIT License.
*/
import * as fs from 'node:fs';
import 'mocha';
import {assert} from 'chai';
import { makePathToFixture } from '../helpers/index.js';
@ -8,13 +11,13 @@ import { KPJFileWriter } from '../../src/types/kpj/kpj-file-writer.js';
import { KeymanDeveloperProjectOptions } from '../../src/types/kpj/keyman-developer-project.js';
import { SymbolUtils } from '../../src/symbol-utils.js';
const callbacks = new TestCompilerCallbacks();
describe('kpj-file-writer', function () {
const callbacks = new TestCompilerCallbacks(this);
it('kpj-file-writer should write a valid v1.0 file', async function() {
const kpjPath = 'khmer_angkor.kpj';
const path = makePathToFixture('kpj', kpjPath);
const input = fs.readFileSync(path);
const input = fs.readFileSync(path) as Uint8Array;
const reader = new KPJFileReader(callbacks);
const inputKpj = reader.read(input);
reader.validate(inputKpj);
@ -53,7 +56,7 @@ describe('kpj-file-writer', function () {
it('kpj-file-writer should write a valid v2.0 file', async function() {
const kpjPath = 'khmer_angkor.kpj';
const path = makePathToFixture('kpj', kpjPath);
const input = fs.readFileSync(path);
const input = fs.readFileSync(path) as Uint8Array;
const reader = new KPJFileReader(callbacks);
const inputKpj = reader.read(input);
reader.validate(inputKpj);

View file

@ -4,7 +4,7 @@
* Created by mcdurdin on 2024-10-16
*/
import * as fs from 'fs';
import * as fs from 'node:fs';
import 'mocha';
import {assert} from 'chai';
@ -17,11 +17,13 @@ import { KpsFileWriter } from '../../src/types/kps/kps-file-writer.js';
import { SymbolUtils } from '../../src/symbol-utils.js';
import { DeveloperUtilsMessages } from '../../src/developer-utils-messages.js';
const callbacks = new TestCompilerCallbacks();
describe('kps-file-reader', function () {
const callbacks = new TestCompilerCallbacks(this);
it('kps-file-reader should read a valid file', function() {
const input = fs.readFileSync(makePathToFixture('kps', 'khmer_angkor.kps'));
const input = fs.readFileSync(makePathToFixture('kps', 'khmer_angkor.kps')) as Uint8Array;
const reader = new KpsFileReader(callbacks);
const kps = reader.read(input);
@ -58,7 +60,7 @@ describe('kps-file-reader', function () {
});
it('kps-file-reader should round-trip with kps-file-writer', function() {
const input = fs.readFileSync(makePathToFixture('kps', 'khmer_angkor.kps'));
const input = fs.readFileSync(makePathToFixture('kps', 'khmer_angkor.kps')) as Uint8Array;
const reader = new KpsFileReader(callbacks);
// Remove XML metadata symbols to reduce clutter for testing purposes
const kps = SymbolUtils.removeSymbols(reader.read(input));
@ -75,7 +77,7 @@ describe('kps-file-reader', function () {
// ERROR_InvalidPackageFile
it('should generate ERROR_InvalidPackageFile if package source file contains invalid XML', async function() {
const input = fs.readFileSync(makePathToFixture('kps', 'error_invalid_package_file.kps'));
const input = fs.readFileSync(makePathToFixture('kps', 'error_invalid_package_file.kps')) as Uint8Array;
const reader = new KpsFileReader(callbacks);
const kps = reader.read(input);
@ -83,4 +85,16 @@ describe('kps-file-reader', function () {
assert.lengthOf(callbacks.messages, 1);
assert.isTrue(callbacks.hasMessage(DeveloperUtilsMessages.ERROR_InvalidPackageFile));
});
// ERROR_NotAPackageFile
it(`should generate ERROR_NotAPackageFile when the package source file is valid XML but does not have a <Package> root element`, function () {
const input = fs.readFileSync(makePathToFixture('kps', 'error_not_a_package_file.kps')) as Uint8Array;
const reader = new KpsFileReader(callbacks);
const kps = reader.read(input);
assert.isNull(kps);
assert.isTrue(callbacks.hasMessage(DeveloperUtilsMessages.ERROR_NotAPackageFile));
});
});

View file

@ -29,8 +29,6 @@
},
"devDependencies": {
"@keymanapp/resources-gosh": "*",
"@types/mocha": "^5.2.7",
"@types/node": "^20.4.1",
"c8": "^7.12.0",
"chalk": "^2.4.2",
"typescript": "^5.4.5"

View file

@ -11,17 +11,7 @@ import { AnalyzeOskCharacterUse } from '../src/osk-character-use/index.js';
describe('AnalyzerMessages', function () {
const callbacks = new TestCompilerCallbacks();
this.beforeEach(function() {
callbacks.clear();
});
this.afterEach(function() {
if(this.currentTest?.isFailed()) {
callbacks.printMessages();
}
});
const callbacks = new TestCompilerCallbacks(this);
it('should have a valid AnalyzerMessages object', function() {
return verifyCompilerMessagesObject(AnalyzerMessages, CompilerErrorNamespace.Analyzer);

View file

@ -7,23 +7,13 @@ import { AnalyzeOskCharacterUse } from '../src/osk-character-use/index.js';
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
describe('AnalyzeOskCharacterUse output formats', function() {
const callbacks = new TestCompilerCallbacks();
const callbacks = new TestCompilerCallbacks(this);
const dummyStrings = {
'a': [{ filename: 'file1.kvks', count: 1 }],
'b': [{ filename: 'file2.kvks', count: 2 }]
};
this.beforeEach(function() {
callbacks.clear();
});
this.afterEach(function() {
if (this.currentTest?.isFailed()) {
callbacks.printMessages();
}
});
it('generates .txt format correctly', function() {
const a = new AnalyzeOskCharacterUse(callbacks, { includeCounts: true });
a.unitTestEndPoints.addStrings(['a', 'b'], 'testfile.kvks');

View file

@ -9,7 +9,7 @@ import { AnalyzerMessages } from '../src/analyzer-messages.js';
import { makePathToFixture } from './helpers/index.js';
describe('AnalyzeOskCharacterUse warnings', function() {
const callbacks = new TestCompilerCallbacks();
const callbacks = new TestCompilerCallbacks(this);
const MOCK_MAP_NO_COUNTS = makePathToFixture(
'osk-character-use',
@ -20,16 +20,6 @@ describe('AnalyzeOskCharacterUse warnings', function() {
'mock-map-with-counts.json'
);
this.beforeEach(function() {
callbacks.clear();
});
this.afterEach(function() {
if (this.currentTest?.isFailed()) {
callbacks.printMessages();
}
});
it('warns if previous map did not include counts but includeCounts=true', function() {
const a = new AnalyzeOskCharacterUse(callbacks, {
includeCounts: true
@ -37,7 +27,7 @@ describe('AnalyzeOskCharacterUse warnings', function() {
const result = a.unitTestEndPoints.loadPreviousMap(MOCK_MAP_NO_COUNTS);
assert.isNotNull(result, 'Expected map to be loaded successfully');
assert.isTrue(
callbacks.hasMessage(AnalyzerMessages.WARN_PreviousMapDidNotIncludeCounts),
'Expected Warn_PreviousMapDidNotIncludeCounts warning'

View file

@ -17,17 +17,7 @@ const SIL_KHMER_KVKS = makePathToFixture('khmer', "sil_khmer.kvks");
const KBDKHMR_WITH_SIL_KHMER_JSON = makePathToFixture('khmer', "KbdKhmr-with-sil_khmer.json");
describe('AnalyzeOskCharacterUse', function () {
const callbacks = new TestCompilerCallbacks();
this.beforeEach(function() {
callbacks.clear();
});
this.afterEach(function() {
if(this.currentTest?.isFailed()) {
callbacks.printMessages();
}
});
const callbacks = new TestCompilerCallbacks(this);
it('generates a mapping file from 2 input files', async function() {
// functional test

View file

@ -34,7 +34,6 @@
"devDependencies": {
"@keymanapp/developer-test-helpers": "*",
"@keymanapp/resources-gosh": "*",
"@types/mocha": "^5.2.7",
"@types/node": "^20.4.1",
"@types/semver": "^7.3.12",
"ajv": "^8.12.0",

View file

@ -201,7 +201,7 @@ export class KeymanProjectCopier implements KeymanCompiler {
*/
private getLocalFolderProject(source: string): string {
source = this.normalizePath(source);
const projectSource = this.normalizePath(this.callbacks.path.join(source, this.callbacks.path.basename(source) + KeymanFileTypes.Source.Project));
const projectSource = this.normalizePath(this.callbacks.path.resolve(this.callbacks.path.join(source, this.callbacks.path.basename(source) + KeymanFileTypes.Source.Project)));
if(!this.callbacks.fs.existsSync(projectSource)) {
this.callbacks.reportMessage(CopierMessages.Error_CannotFindInputProject({project: source}));
return null;
@ -216,7 +216,7 @@ export class KeymanProjectCopier implements KeymanCompiler {
* @returns
*/
private getLocalFileProject(source: string): string {
return this.normalizePath(source);
return this.normalizePath(this.callbacks.path.resolve(source));
}
/**

View file

@ -14,17 +14,7 @@ import { KeymanProjectCopier } from '../src/KeymanProjectCopier.js';
import { makePathToFixture } from './helpers/index.js';
describe('CopierMessages', function () {
const callbacks = new TestCompilerCallbacks(makePathToFixture('online'));
this.beforeEach(function() {
callbacks.clear();
});
this.afterEach(function() {
if(this.currentTest?.isFailed()) {
callbacks.printMessages();
}
});
const callbacks = new TestCompilerCallbacks(this, makePathToFixture('online'));
it('should have a valid CopierMessages object', function() {
return verifyCompilerMessagesObject(CopierMessages, CompilerErrorNamespace.Copier);

View file

@ -23,7 +23,7 @@ function normalizeNewLine(s: string): string {
}
describe('KeymanProjectCopier', function() {
const callbacks = new TestCompilerCallbacks(makePathToFixture('online'));
const callbacks = new TestCompilerCallbacks(this, makePathToFixture('online'));
this.beforeAll(function() {
if(TEST_SAVE_ARTIFACTS) {
@ -32,16 +32,6 @@ describe('KeymanProjectCopier', function() {
}
});
this.beforeEach(function() {
callbacks.clear();
});
this.afterEach(function() {
if(this.currentTest?.isFailed()) {
callbacks.printMessages();
}
});
const tests = [
//

View file

@ -3,26 +3,16 @@
*/
import 'mocha';
import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs';
import * as path from 'node:path';
import * as os from 'node:os';
import * as fs from 'node:fs';
import { assert } from 'chai';
import { AbstractGenerator, GeneratorArtifacts } from '../src/abstract-generator.js';
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
import { options } from './shared-options.js';
describe('AbstractGenerator', function () {
const callbacks = new TestCompilerCallbacks();
this.beforeEach(function() {
callbacks.clear();
});
this.afterEach(function() {
if(this.currentTest.isFailed()) {
callbacks.printMessages();
}
});
const callbacks = new TestCompilerCallbacks(this);
it('should write out files successfully', async function() {
const ag = new AbstractGenerator();
@ -40,7 +30,7 @@ describe('AbstractGenerator', function () {
};
assert.isTrue(await ag.write(artifacts));
assert.isTrue(fs.existsSync(filename));
const buf = fs.readFileSync(filename);
const buf = fs.readFileSync(filename) as Uint8Array;
assert.deepEqual(buf, data);
fs.unlinkSync(filename);

View file

@ -32,9 +32,10 @@ class BasicGeneratorTest extends BasicGenerator {
}
describe('BasicGenerator', function () {
const callbacks = new TestCompilerCallbacks(this);
it('should configure default settings', async function() {
const bg = new BasicGenerator();
const callbacks = new TestCompilerCallbacks();
assert.isTrue(await bg.init(callbacks, options));
assert.isTrue(bg.test_preGenerate());
@ -45,7 +46,6 @@ describe('BasicGenerator', function () {
const bg = new BasicGeneratorTest();
bg.setup();
const callbacks = new TestCompilerCallbacks();
assert.isTrue(await bg.init(callbacks, options));
assert.isTrue(bg.test_preGenerate());

View file

@ -22,17 +22,7 @@ import { LexicalModelGenerator } from '../src/lexical-model-generator.js';
import { options } from './shared-options.js';
describe('GeneratorMessages', function () {
const callbacks = new TestCompilerCallbacks();
this.beforeEach(function() {
callbacks.clear();
});
this.afterEach(function() {
if(this.currentTest.isFailed()) {
callbacks.printMessages();
}
});
const callbacks = new TestCompilerCallbacks(this);
it('should have a valid GeneratorMessages object', function() {
return verifyCompilerMessagesObject(GeneratorMessages, CompilerErrorNamespace.Generator);
@ -78,7 +68,6 @@ describe('GeneratorMessages', function () {
targets: ['invalid']
};
const bg = new BasicGenerator();
const callbacks = new TestCompilerCallbacks();
assert(await bg.init(callbacks, testOptions));
assert.isFalse(bg.test_preGenerate());
assert.isTrue(callbacks.hasMessage(GeneratorMessages.ERROR_InvalidTarget));
@ -89,7 +78,6 @@ describe('GeneratorMessages', function () {
id: '???invalid',
};
const kkg = new KeymanKeyboardGenerator();
const callbacks = new TestCompilerCallbacks();
assert.isTrue(await kkg.init(callbacks, testOptions));
assert.isNull(await kkg.run());
assert.isTrue(callbacks.hasMessage(GeneratorMessages.ERROR_InvalidKeymanKeyboardId));
@ -100,7 +88,6 @@ describe('GeneratorMessages', function () {
id: '???invalid',
};
const lkg = new LdmlKeyboardGenerator();
const callbacks = new TestCompilerCallbacks();
assert.isTrue(await lkg.init(callbacks, testOptions));
assert.isNull(await lkg.run());
assert.isTrue(callbacks.hasMessage(GeneratorMessages.ERROR_InvalidLdmlKeyboardId));
@ -111,7 +98,6 @@ describe('GeneratorMessages', function () {
id: 'example.???.invalid',
};
const lmg = new LexicalModelGenerator();
const callbacks = new TestCompilerCallbacks();
assert.isTrue(await lmg.init(callbacks, testOptions));
assert.isNull(await lmg.run());
assert.isTrue(callbacks.hasMessage(GeneratorMessages.ERROR_InvalidLexicalModelId));

View file

@ -33,6 +33,8 @@ function getFilenames(p: string, base?: string): string[] {
describe('KeymanKeyboardGenerator', function () {
let clock: sinon.SinonFakeTimers;
const callbacks = new TestCompilerCallbacks(this);
before(function() {
// We will always be 12 April 2024 to match test fixtures
clock = sinon.useFakeTimers(new Date(2024, 3, 12));
@ -44,7 +46,6 @@ describe('KeymanKeyboardGenerator', function () {
it('should generate a Keyman keyboard from provided options', async function() {
const generator = new KeymanKeyboardGenerator();
const callbacks = new TestCompilerCallbacks();
assert(await generator.init(callbacks, options));
const result = await generator.run();
assert.exists(result);

View file

@ -33,6 +33,8 @@ function getFilenames(p: string, base?: string): string[] {
describe('LdmlKeyboardGenerator', function () {
let clock: sinon.SinonFakeTimers;
const callbacks = new TestCompilerCallbacks(this);
before(function() {
// We will always be 12 April 2024 to match test fixtures
clock = sinon.useFakeTimers(new Date(2024, 3, 12));
@ -44,7 +46,6 @@ describe('LdmlKeyboardGenerator', function () {
it('should generate a LDML keyboard from provided options', async function() {
const generator = new LdmlKeyboardGenerator();
const callbacks = new TestCompilerCallbacks();
assert(await generator.init(callbacks, options));
const result = await generator.run();
assert.exists(result);

View file

@ -35,6 +35,8 @@ function getFilenames(p: string, base?: string): string[] {
describe('LexicalModelGenerator', function () {
let clock: sinon.SinonFakeTimers;
const callbacks = new TestCompilerCallbacks(this);
before(function() {
// We will always be 12 April 2024 to match test fixtures
clock = sinon.useFakeTimers(new Date(2024, 3, 12));
@ -46,7 +48,6 @@ describe('LexicalModelGenerator', function () {
it('should generate a lexical model from provided options', async function() {
const generator = new LexicalModelGenerator();
const callbacks = new TestCompilerCallbacks();
const opts: GeneratorOptions = {...options};
opts.id = 'sample.en.sample';
opts.targets = [KeymanTargets.KeymanTarget.any];

View file

@ -29,8 +29,6 @@
"@keymanapp/langtags": "*"
},
"devDependencies": {
"@types/mocha": "^5.2.7",
"@types/node": "^20.4.1",
"c8": "^7.12.0",
"chalk": "^2.4.2",
"typescript": "^5.4.5"

View file

@ -8,8 +8,6 @@ import { makePathToFixture } from './helpers/index.js';
import { KeyboardInfoCompiler } from '../src/keyboard-info-compiler.js';
import { KeyboardInfoFile } from '../src/keyboard-info-file.js';
const callbacks = new TestCompilerCallbacks();
const KHMER_ANGKOR_JS = makePathToFixture('khmer_angkor', 'build', 'khmer_angkor.js');
const KHMER_ANGKOR_KPS = makePathToFixture('khmer_angkor', 'source', 'khmer_angkor.kps');
const KHMER_ANGKOR_KMP = makePathToFixture('khmer_angkor', 'build', 'khmer_angkor.kmp');
@ -24,15 +22,7 @@ const KHMER_ANGKOR_SOURCES = {
describe('KeyboardInfoCompilerMessages', function () {
this.beforeEach(function() {
callbacks.clear();
});
this.afterEach(function() {
if(this.currentTest?.isFailed()) {
callbacks.printMessages();
}
})
const callbacks = new TestCompilerCallbacks(this);
it('should have a valid KeyboardInfoCompilerMessages object', function() {
return verifyCompilerMessagesObject(KeyboardInfoCompilerMessages, CompilerErrorNamespace.KeyboardInfoCompiler);

View file

@ -10,18 +10,6 @@ import { KMX, KeymanFileTypes, KeymanTargets, KmpJsonFile } from '@keymanapp/com
import { CompilerCallbacks } from '@keymanapp/developer-utils';
import { KeyboardInfoFile, KeyboardInfoFileLanguage, KeyboardInfoFilePlatform } from './keyboard-info-file.js';
const callbacks = new TestCompilerCallbacks();
beforeEach(function() {
callbacks.clear();
});
afterEach(function() {
if(this.currentTest?.isFailed()) {
callbacks.printMessages();
}
});
const KHMER_ANGKOR_KPJ = makePathToFixture('khmer_angkor', 'khmer_angkor.kpj');
const KHMER_ANGKOR_JS = makePathToFixture('khmer_angkor', 'build', 'khmer_angkor.js');
const KHMER_ANGKOR_KPS = makePathToFixture('khmer_angkor', 'source', 'khmer_angkor.kps');
@ -87,6 +75,9 @@ const JAVA_DISPLAY_FONT_INFO = { family: "Java", source: [ JAVA_DISPLAY_FONT ] }
const JAVA_OSK_FONT_INFO = { family: "Java Kbd", source: [ JAVA_OSK_FONT ] };
describe('keyboard-info-compiler', function () {
const callbacks = new TestCompilerCallbacks(this);
it('compile a .keyboard_info file correctly', async function() {
const kpjFilename = KHMER_ANGKOR_KPJ;
const buildKeyboardInfoFilename = makePathToFixture('khmer_angkor', 'build', 'khmer_angkor.keyboard_info');
@ -844,6 +835,9 @@ describe('keyboard-info-compiler', function () {
});
describe('fillLanguageMetadata', function() {
const callbacks = new TestCompilerCallbacks(this);
const tests: { bcp47: string, lang: KeyboardInfoFileLanguage, commonScript: string }[] = [
// 'und' language subtag

View file

@ -32,8 +32,6 @@
},
"devDependencies": {
"@keymanapp/developer-test-helpers": "*",
"@types/mocha": "^5.2.7",
"@types/node": "^20.4.1",
"@types/semver": "^7.3.12",
"@types/sinon": "^10.0.13",
"@types/sinon-chai": "^3.2.9",

View file

@ -11,6 +11,8 @@ const keyboardsDir = __dirname + '/../../../../../common/test/keyboards/';
const baselineDir = keyboardsDir + 'baseline/';
describe('Compiler class', function() {
const callbacks = new TestCompilerCallbacks(this);
it('should throw on failure', async function() {
const compiler = new KmnCompiler();
const callbacks : any = null; // ERROR
@ -25,14 +27,12 @@ describe('Compiler class', function() {
it('should start', async function() {
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks, null));
assert(compiler.verifyInitialized());
});
it('should compile a basic keyboard', async function() {
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks, {saveDebug: true, shouldAddCompilerVersion: false}));
assert(compiler.verifyInitialized());
@ -60,7 +60,6 @@ describe('Compiler class', function() {
it('should build all baseline fixtures', async function() {
this.timeout(10000); // there are quite a few fixtures, sometimes CI agents are slow
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks, {saveDebug: true, shouldAddCompilerVersion: false}));
assert(compiler.verifyInitialized());
@ -90,7 +89,6 @@ describe('Compiler class', function() {
it('should compile a keyboard with visual keyboard', async function() {
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert.isTrue(await compiler.init(callbacks, {
saveDebug: true,
shouldAddCompilerVersion: false,

View file

@ -7,19 +7,14 @@ import { KMX, KmxFileReader } from '@keymanapp/common-types';
describe('Keyboard compiler features', function() {
let compiler: KmnCompiler = null;
let callbacks: TestCompilerCallbacks = null;
const callbacks = new TestCompilerCallbacks(this);
this.beforeAll(async function() {
compiler = new KmnCompiler();
callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks, {saveDebug: true}));
assert(compiler.verifyInitialized());
});
beforeEach(function() {
callbacks.clear();
});
// Test each Keyman file version target
const versions = [

View file

@ -7,7 +7,7 @@ import { KmnCompilerMessageRanges, KmnCompilerMessages } from '../src/compiler/k
import { makePathToFixture } from './helpers/index.js';
describe('KmnCompilerMessages', function () {
const callbacks = new TestCompilerCallbacks();
const callbacks = new TestCompilerCallbacks(this);
it('should have a valid KmnCompilerMessages object', function() {
return verifyCompilerMessagesObject(KmnCompilerMessages, CompilerErrorNamespace.KmnCompiler);

View file

@ -25,7 +25,7 @@ const generateTestFilenames = (id: string) => ({
});
describe('KeymanWeb Compiler', function() {
const callbacks = new TestCompilerCallbacks();
const callbacks = new TestCompilerCallbacks(this);
const kmnCompiler: KmnCompiler = new KmnCompiler();
this.beforeAll(async function() {
@ -35,13 +35,6 @@ describe('KeymanWeb Compiler', function() {
}));
});
this.afterEach(function() {
if(this.currentTest?.isFailed() || debug) {
callbacks.printMessages();
}
callbacks.clear();
});
it('should compile a complex keyboard', async function() {
await run_test_keyboard(kmnCompiler, 'khmer_angkor');
});

View file

@ -7,7 +7,7 @@ import { KmnCompiler } from '../../src/main.js';
import { CompilerErrorNamespace } from '@keymanapp/developer-utils';
describe('KmwCompilerMessages', function () {
const callbacks = new TestCompilerCallbacks();
const callbacks = new TestCompilerCallbacks(this);
it('should have a valid KmwCompilerMessages object', function() {
return verifyCompilerMessagesObject(KmwCompilerMessages, CompilerErrorNamespace.KmwCompiler);

View file

@ -6,6 +6,8 @@ import { KmnCompilerMessages } from '../src/compiler/kmn-compiler-messages.js';
import { compilerErrorFormatCode } from '@keymanapp/developer-utils';
describe('Compiler UnicodeSet function', function() {
const callbacks = new TestCompilerCallbacks(this);
it('should fixup "short" \\u{} escapes', function () {
assert.equal(KmnCompiler.fixNewPattern(`\\u{A}`), `\\u000A`); // "
assert.equal(KmnCompiler.fixNewPattern(`\\u{22}`), `\\u0022`); // "
@ -24,14 +26,12 @@ describe('Compiler UnicodeSet function', function() {
it('should start', async function() {
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks, null));
assert(compiler.verifyInitialized());
});
it('should compile a basic uset', async function() {
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks, null));
assert(compiler.verifyInitialized());
@ -49,7 +49,6 @@ describe('Compiler UnicodeSet function', function() {
});
it('should compile a more complex uset', async function() {
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks, null));
assert(compiler.verifyInitialized());
@ -69,7 +68,6 @@ describe('Compiler UnicodeSet function', function() {
});
it('should compile an even more complex uset', async function() {
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks, null));
assert(compiler.verifyInitialized());
@ -96,7 +94,6 @@ describe('Compiler UnicodeSet function', function() {
});
it('should fail in various ways', async function() {
const compiler = new KmnCompiler();
const callbacks = new TestCompilerCallbacks();
assert(await compiler.init(callbacks, null));
assert(compiler.verifyInitialized());
// map from string to failing error

View file

@ -35,8 +35,6 @@
"@keymanapp/developer-test-helpers": "*",
"@keymanapp/resources-gosh": "*",
"@types/common-tags": "^1.8.4",
"@types/mocha": "^5.2.7",
"@types/node": "^20.4.1",
"@types/semver": "^7.3.12",
"abnf": "^4.3.1",
"c8": "^7.12.0",

View file

@ -34,8 +34,6 @@
"@keymanapp/developer-utils": "*"
},
"devDependencies": {
"@types/mocha": "^5.2.7",
"@types/node": "^20.4.1",
"c8": "^7.12.0",
"chalk": "^2.4.2",
"typescript": "^5.4.5"

View file

@ -6,13 +6,9 @@ import { makePathToFixture } from './helpers/index.js';
import { ModelInfoCompiler } from '../src/model-info-compiler.js';
import { KmpCompiler } from '@keymanapp/kmc-package';
const callbacks = new TestCompilerCallbacks();
beforeEach(function() {
callbacks.clear();
});
describe('model-info-compiler', function () {
const callbacks = new TestCompilerCallbacks(this);
it('compile a .model_info file correctly', async function() {
const kpjFilename = makePathToFixture('sil.cmo.bw', 'sil.cmo.bw.model.kpj');
const kpsFilename = makePathToFixture('sil.cmo.bw', 'source', 'sil.cmo.bw.model.kps');

View file

@ -37,8 +37,6 @@
"devDependencies": {
"@keymanapp/developer-test-helpers": "*",
"@keymanapp/models-templates": "*",
"@types/mocha": "^5.2.7",
"@types/node": "^20.4.1",
"c8": "^7.12.0",
"chalk": "^2.4.2",
"esbuild": "^0.25.0"

View file

@ -7,7 +7,7 @@ import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
import { LexicalModelTypes } from '@keymanapp/common-types';
describe('LexicalModelCompiler - pseudoclosure compilation + use', function () {
const callbacks = new TestCompilerCallbacks();
const callbacks = new TestCompilerCallbacks(this);
const MODEL_ID = 'example.qaa.trivial';
const PATH = makePathToFixture(MODEL_ID);

View file

@ -7,7 +7,7 @@ import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
import { KeymanFileTypes } from '@keymanapp/common-types';
describe('LexicalModelCompiler', function () {
const callbacks = new TestCompilerCallbacks();
const callbacks = new TestCompilerCallbacks(this);
this.timeout(5000);

View file

@ -11,10 +11,7 @@ import { TrieModel } from '@keymanapp/models-templates';
import { LexicalModelTypes } from '@keymanapp/common-types';
describe('LexicalModelCompiler', function () {
const callbacks = new TestCompilerCallbacks();
this.beforeEach(function() {
callbacks.clear();
});
const callbacks = new TestCompilerCallbacks(this);
describe('#generateLexicalModelCode', function () {
it('should compile a trivial word list', async function () {

View file

@ -8,17 +8,7 @@ import { makePathToFixture } from './helpers/index.js';
describe('ModelCompilerMessages', function () {
const callbacks = new TestCompilerCallbacks();
this.beforeEach(function() {
callbacks.clear();
});
this.afterEach(function() {
if(this.currentTest?.isFailed()) {
callbacks.printMessages();
}
});
const callbacks = new TestCompilerCallbacks(this);
it('should have a valid ModelCompilerMessages object', function() {
return verifyCompilerMessagesObject(ModelCompilerMessages, CompilerErrorNamespace.ModelCompiler);

View file

@ -22,11 +22,10 @@ const SENCOTEN_WORDLIST = {
describe('parsing a word list', function () {
const testCallbacks = new TestCompilerCallbacks();
const callbacks = new TestCompilerCallbacks(this);
beforeEach(function () {
testCallbacks.clear();
setCompilerCallbacks(testCallbacks);
setCompilerCallbacks(callbacks);
});
afterEach(function () {
@ -43,12 +42,12 @@ describe('parsing a word list', function () {
const withoutBOM: WordList = {};
parseWordListFromContents(withoutBOM, file);
assert.deepEqual(withoutBOM, expected, "expected regular file to parse properly");
assert.isEmpty(testCallbacks.messages);
assert.isEmpty(callbacks.messages);
const withBOM: WordList = {};
parseWordListFromContents(withBOM, `${BOM}${file}`)
assert.deepEqual(withBOM, expected, "expected BOM to be ignored");
assert.isEmpty(testCallbacks.messages);
assert.isEmpty(callbacks.messages);
});
it('should read word lists in UTF-8', function () {
@ -58,7 +57,7 @@ describe('parsing a word list', function () {
parseWordListFromFilename(wordlist, filename);
assert.deepEqual(wordlist, SENCOTEN_WORDLIST);
assert.isEmpty(testCallbacks.messages);
assert.isEmpty(callbacks.messages);
});
it('should read word lists in UTF-16 little-endian (with BOM)', function () {
@ -69,7 +68,7 @@ describe('parsing a word list', function () {
parseWordListFromFilename(wordlist, filename);
assert.deepEqual(wordlist, SENCOTEN_WORDLIST);
assert.isEmpty(testCallbacks.messages);
assert.isEmpty(callbacks.messages);
});
it('should NOT read word lists in UTF-16 big-endian (with BOM)', function () {
@ -106,22 +105,22 @@ describe('parsing a word list', function () {
assert.deepEqual(repeatedWords, expected);
assert.lengthOf(testCallbacks.messages, 4);
assert.lengthOf(callbacks.messages, 4);
// hello has been seen multiple times:
assert.isTrue(testCallbacks.hasMessage(ModelCompilerMessages.HINT_DuplicateWordInSameFile));
assert.isTrue(callbacks.hasMessage(ModelCompilerMessages.HINT_DuplicateWordInSameFile));
// helló and hello + U+0301 have both been seen:
assert.isTrue(testCallbacks.hasMessage(ModelCompilerMessages.HINT_MixedNormalizationForms));
assert.isTrue(callbacks.hasMessage(ModelCompilerMessages.HINT_MixedNormalizationForms));
// Let's parse another file:
testCallbacks.clear();
callbacks.clear();
// Now, parse a DIFFERENT file, but with an NFD entry.
parseWordListFromContents(repeatedWords, "hello\u0301\t5\n");
assert.lengthOf(testCallbacks.messages, 1);
assert.lengthOf(callbacks.messages, 1);
// hello + U+0301 (NFD) has been seen, but...
assert.isTrue(testCallbacks.hasMessage(ModelCompilerMessages.HINT_MixedNormalizationForms));
assert.isTrue(callbacks.hasMessage(ModelCompilerMessages.HINT_MixedNormalizationForms));
// BUT! We have not seen a duplicate **within the same file**
assert.isFalse(testCallbacks.hasMessage(ModelCompilerMessages.HINT_DuplicateWordInSameFile));
assert.isFalse(callbacks.hasMessage(ModelCompilerMessages.HINT_DuplicateWordInSameFile));
assert.deepEqual(repeatedWords, {
hello: expected['hello'],

View file

@ -6,12 +6,13 @@ import { makePathToFixture, compileModelSourceCode } from './helpers/index.js';
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
describe('LexicalModelCompiler', function () {
const callbacks = new TestCompilerCallbacks(this);
describe('specifying punctuation', function () {
const MODEL_ID = 'example.qaa.trivial';
const PATH = makePathToFixture(MODEL_ID);
it('should compile punctuation into the generated code', async function () {
const callbacks = new TestCompilerCallbacks();
const compiler = new LexicalModelCompiler();
assert.isTrue(await compiler.init(callbacks, null));

View file

@ -36,8 +36,6 @@
},
"devDependencies": {
"@keymanapp/developer-test-helpers": "*",
"@types/mocha": "^5.2.7",
"@types/node": "^20.4.1",
"c8": "^7.12.0",
"chalk": "^2.4.2",
"typescript": "^5.4.5"

View file

@ -206,7 +206,8 @@ export class KmpCompiler implements KeymanCompiler {
fileVersion: null,
keymanDeveloperVersion: KEYMAN_VERSION.VERSION
},
options: {}
options: {},
info: {},
};
//

View file

@ -70,7 +70,7 @@ export class PackageValidation {
}
private checkForModelsAndKeyboardsInSamePackage(kmpJson: KmpJsonFile.KmpJsonFile): boolean {
if(kmpJson.lexicalModels?.length > 0 && kmpJson.keyboards?.length > 0) {
if((kmpJson.lexicalModels?.length ?? 0) > 0 && (kmpJson.keyboards?.length ?? 0) > 0) {
this.callbacks.reportMessage(PackageCompilerMessages.Error_PackageCannotContainBothModelsAndKeyboards());
return false;
}
@ -120,7 +120,7 @@ export class PackageValidation {
}
for(const keyboard of kmpJson.keyboards) {
if(!this.checkForDuplicatedOrNonMinimalLanguages('keyboard', keyboard.id, keyboard.languages)) {
if(!this.checkForDuplicatedOrNonMinimalLanguages('keyboard', keyboard.id, keyboard.languages ?? [])) {
return false;
}
// Note: package-version-validation verifies that there is a corresponding
@ -131,7 +131,7 @@ export class PackageValidation {
}
private checkContentFiles(kmpJson: KmpJsonFile.KmpJsonFile, outputFilename: string): boolean {
for(const file of kmpJson.files) {
for(const file of kmpJson.files ?? []) {
if(!this.checkContentFile(file, outputFilename)) {
return false;
}
@ -202,7 +202,7 @@ export class PackageValidation {
return true;
}
private checkPackageInfo(file: KmpJsonFile.KmpJsonFile) {
private checkPackageInfo(file: KmpJsonFile.KmpJsonFile): boolean {
if(!file.info || !file.info.name || !file.info.name.description.trim()) {
this.callbacks.reportMessage(PackageCompilerMessages.Error_PackageNameCannotBeBlank());
return false;
@ -214,11 +214,11 @@ export class PackageValidation {
/* c8 ignore next 3 */
if (match === null) {
this.callbacks.reportMessage(PackageCompilerMessages.Error_InvalidAuthorEmail({email:file.info.author.url}));
return null;
return false;
}
if(!isValidEmail(match[2])) {
this.callbacks.reportMessage(PackageCompilerMessages.Error_InvalidAuthorEmail({email:file.info.author.url}));
return null;
return false;
}
}

View file

@ -10,15 +10,10 @@ import { makePathToFixture } from './helpers/index.js';
import { KmpCompiler } from '../src/compiler/kmp-compiler.js';
import { CompilerErrorNamespace, CompilerOptions } from '@keymanapp/developer-utils';
const callbacks = new TestCompilerCallbacks();
describe('PackageCompilerMessages', function () {
this.afterEach(function() {
if(this.currentTest.isFailed()) {
callbacks.printMessages();
}
});
const callbacks = new TestCompilerCallbacks(this);
it('should have a valid PackageCompilerMessages object', function() {
return verifyCompilerMessagesObject(PackageCompilerMessages, CompilerErrorNamespace.PackageCompiler);

View file

@ -21,22 +21,14 @@ describe('KmpCompiler', function () {
'example.qaa.sencoten',
'withfolders.qaa.sencoten',
];
const callbacks = new TestCompilerCallbacks();
const callbacks = new TestCompilerCallbacks(this);
let kmpCompiler: KmpCompiler = null;
this.beforeAll(async function() {
callbacks.clear();
kmpCompiler = new KmpCompiler();
assert.isTrue(await kmpCompiler.init(callbacks, null));
});
this.afterEach(function() {
if(this.currentTest?.isFailed()) {
callbacks.printMessages();
}
callbacks.clear();
});
for (const modelID of MODELS) {
const kpsPath = modelID.includes('withfolders') ?
makePathToFixture(modelID, 'source', `${modelID}.model.kps`) : makePathToFixture(modelID, `${modelID}.model.kps`);
@ -144,8 +136,6 @@ describe('KmpCompiler', function () {
*/
it(`should transform a .kps file for a keyboard package to a correct kmp.json`, function () {
callbacks.clear();
const kpsPath = makePathToFixture('kmp.json', 'ahom_star.kps');
const kmpJsonRefPath = makePathToFixture('kmp.json', 'kmp.json');
@ -171,13 +161,9 @@ describe('KmpCompiler', function () {
});
it(`should support .kps 17.0 metadata correctly`, function () {
callbacks.clear();
const kpsPath = makePathToFixture('kmp_2.0', 'khmer_angkor.kps');
const kmpJsonRefPath = makePathToFixture('kmp_2.0', 'kmp.json');
debugger;
const kmpJsonActual = kmpCompiler.transformKpsToKmpObject(kpsPath);
if(kmpJsonActual == null) {
callbacks.printMessages();
@ -205,8 +191,6 @@ describe('KmpCompiler', function () {
it('should warn on absolute paths', async function() {
this.timeout(10000); // building a zip file can sometimes be slow
callbacks.clear();
const kpsPath = makePathToFixture('absolute_path', 'source', 'absolute_path.kps');
const kmpCompiler = new KmpCompiler();
assert.isTrue(await kmpCompiler.init(callbacks, null));
@ -233,8 +217,6 @@ describe('KmpCompiler', function () {
it('should normalize DOS pathnames from \\ to /', async function() {
// this.timeout(10000); // building a zip file can sometimes be slow
callbacks.clear();
const kpsPath = makePathToFixture('normalize_paths', 'source', 'khmer_angkor.kps');
const kmpCompiler = new KmpCompiler();
assert.isTrue(await kmpCompiler.init(callbacks, null));
@ -315,7 +297,7 @@ describe('KmpCompiler', function () {
assert.isFalse(validation.validate(inputFilename, outputFilename, kmpJson));
});
it(`should reject an package that contains itself`, function () {
it(`should reject a package that contains itself`, function () {
const inputFilename = makePathToFixture('invalid', 'error_package_must_not_contain_itself.kps');
const outputFilename = makePathToFixture('invalid', 'error_package_must_not_contain_itself.kmp');
const kmpJson = kmpCompiler.transformKpsToKmpObject(inputFilename);

View file

@ -11,6 +11,7 @@ import { makePathToFixture } from './helpers/index.js';
// test results documented below.
describe('package versioning', function () {
const callbacks = new TestCompilerCallbacks(this);
const cases: [string,string][] = [
['test-single-version-1-package', 'test1.kps'],
@ -33,7 +34,6 @@ describe('package versioning', function () {
for(const [ caseTitle, filename ] of cases) {
it(caseTitle, async function () {
const callbacks = new TestCompilerCallbacks();
const kmpCompiler = new KmpCompiler();
assert.isTrue(await kmpCompiler.init(callbacks, null));

View file

@ -9,6 +9,8 @@ import { makePathToFixture } from './helpers/index.js';
import { WindowsPackageInstallerCompiler, WindowsPackageInstallerSources } from '../src/compiler/windows-package-installer-compiler.js';
describe('WindowsPackageInstallerCompiler', function () {
const callbacks = new TestCompilerCallbacks(this);
it(`should build an SFX archive`, async function () {
this.timeout(10000); // this test can take a little while to run
@ -22,7 +24,6 @@ describe('WindowsPackageInstallerCompiler', function () {
appName: 'Testing',
};
const callbacks = new TestCompilerCallbacks();
const compiler = new WindowsPackageInstallerCompiler();
assert.isTrue(await compiler.init(callbacks, {sources}));
@ -44,7 +45,7 @@ describe('WindowsPackageInstallerCompiler', function () {
const zipBuffer = sfxBuffer.slice(setupExeSize);
// Verify setup.exe sfx loader
const setupExeFixture = fs.readFileSync(sources.setupExeFilename);
const setupExeFixture = fs.readFileSync(sources.setupExeFilename) as Uint8Array;
assert.deepEqual(setupExe, setupExeFixture);
// Load the zip from the buffer
@ -68,7 +69,7 @@ khmer_angkor.kmp
assert.equal(setupInf.trim(), setupInfFixture.trim());
const verifyFile = async (filename: string) => {
const fixture = fs.readFileSync(filename);
const fixture = fs.readFileSync(filename) as Uint8Array;
const file = await zipFile.file(path.basename(filename)).async('uint8array');
assert.deepEqual(file, fixture, `File in zip '${filename}' did not match fixture`);
};

View file

@ -54,8 +54,6 @@
],
"devDependencies": {
"@sentry/cli": "^2.31.0",
"@types/mocha": "^5.2.7",
"@types/node": "^20.4.1",
"c8": "^7.12.0",
"esbuild": "^0.25.0",
"typescript": "^5.4.5"

View file

@ -81,7 +81,7 @@ async function doCopy(callbacks: NodeCompilerCallbacks, sources: string | string
const dest = options.outPath;
callbacks.reportMessage(InfrastructureMessages.Info_CopyingProject({source, dest}));
const result = runCopier(callbacks, source, options);
const result = await runCopier(callbacks, source, options);
if(result) {
callbacks.reportMessage(InfrastructureMessages.Info_ProjectCopiedSuccessfully({source, dest}));
} else {

View file

@ -1,3 +1,6 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*/
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
import { clearOptions } from '../src/util/options.js';
import { assert } from 'chai';
@ -5,8 +8,6 @@ import 'mocha';
import { BuildProject } from '../src/commands/buildClasses/BuildProject.js';
import { makePathToFixture } from './helpers/index.js';
const callbacks = new TestCompilerCallbacks();
interface CompilerWarningsAsErrorsTruthTable {
cli: boolean;
kpj: boolean;
@ -14,8 +15,9 @@ interface CompilerWarningsAsErrorsTruthTable {
};
describe('compilerWarningsAsErrors', function () {
beforeEach(() => {
callbacks.clear();
const callbacks = new TestCompilerCallbacks(this);
this.beforeEach(function() {
clearOptions();
});

View file

@ -1,3 +1,6 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*/
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
import { assert } from 'chai';
import 'mocha';
@ -10,10 +13,7 @@ interface MessageTest {input: string, result: CompilerMessageOverride};
interface InvalidMessageTest {input: string, code: number};
describe('commandOptionsMessageToCompilerOptionsMessage', function () {
const callbacks = new TestCompilerCallbacks();
this.beforeEach(function() {
callbacks.clear();
});
const callbacks = new TestCompilerCallbacks(this);
const valid: MessageTest[] = [
// Test the allowable coercions

View file

@ -1,11 +1,12 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*/
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
import { assert } from 'chai';
import 'mocha';
import { makePathToFixture } from './helpers/index.js';
import { expandFileList, expandFileLists } from '../src/util/fileLists.js';
const callbacks = new TestCompilerCallbacks();
// expandFileList expands each file name relative to the file list's supplied
// filename, so we need to compare against the full path.
const expectedFileList = [
@ -21,11 +22,9 @@ const expectedFiles = [
'file5.kmn'
];
beforeEach(function() {
callbacks.clear();
});
describe('expandFileList', function () {
const callbacks = new TestCompilerCallbacks(this);
it('should report a missing filelist correctly', async function() {
const path = makePathToFixture('file-lists', 'does-not-exist.txt');
@ -45,6 +44,8 @@ describe('expandFileList', function () {
});
describe('expandFileLists', function () {
const callbacks = new TestCompilerCallbacks(this);
it('should splice a filelist in correctly', async function() {
// We just use this to test the splicing so no path resolution is made
const files = [

View file

@ -1,3 +1,6 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*/
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
@ -28,17 +31,15 @@ describe('generateKeymanKeyboard', function() {
description: 'Sample Keyboard Test',
};
const callbacks: TestCompilerCallbacks = new TestCompilerCallbacks();
const callbacks: TestCompilerCallbacks = new TestCompilerCallbacks(this);
let outPath: string = null;
this.beforeEach(function() {
callbacks.clear();
outPath = fs.mkdtempSync(path.join(os.tmpdir(), 'kmc-'));
});
this.afterEach(function() {
if(this.currentTest.isFailed()) {
callbacks.printMessages();
console.error(`Output kept at ${outPath}`);
} else {
if(outPath) fs.rmSync(outPath, {recursive: true, force: true});

View file

@ -1,3 +1,6 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*/
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
@ -28,17 +31,15 @@ describe('generateLdmlKeyboard', function() {
description: 'Sample Keyboard Test',
};
const callbacks: TestCompilerCallbacks = new TestCompilerCallbacks();
const callbacks: TestCompilerCallbacks = new TestCompilerCallbacks(this);
let outPath: string = null;
this.beforeEach(function() {
callbacks.clear();
outPath = fs.mkdtempSync(path.join(os.tmpdir(), 'kmc-'));
});
this.afterEach(function() {
if(this.currentTest.isFailed()) {
callbacks.printMessages();
console.error(`Output kept at ${outPath}`);
} else {
if(outPath) fs.rmSync(outPath, {recursive: true, force: true});

View file

@ -1,3 +1,6 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*/
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
@ -27,17 +30,15 @@ describe('generateLexicalModel', function() {
description: 'Sample Model Test',
};
const callbacks: TestCompilerCallbacks = new TestCompilerCallbacks();
const callbacks: TestCompilerCallbacks = new TestCompilerCallbacks(this);
let outPath: string = null;
this.beforeEach(function() {
callbacks.clear();
outPath = fs.mkdtempSync(path.join(os.tmpdir(), 'kmc-'));
});
this.afterEach(function() {
if(this.currentTest.isFailed()) {
callbacks.printMessages();
console.error(`Output kept at ${outPath}`);
} else {
if(outPath) fs.rmSync(outPath, {recursive: true, force: true});

View file

@ -1,3 +1,6 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*/
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
import { assert } from 'chai';
import 'mocha';
@ -6,9 +9,10 @@ import { makePathToFixture } from './helpers/index.js';
import { InfrastructureMessages } from '../src/messages/infrastructureMessages.js';
import { clearOptions } from '../src/util/options.js';
const callbacks = new TestCompilerCallbacks();
describe('BuildProject', function () {
const callbacks = new TestCompilerCallbacks(this);
it('should build a keyboard project', async function() {
clearOptions();
const builder = new BuildProject();

View file

@ -0,0 +1,2 @@
IMSample.exp
IMSample.lib

View file

@ -52,10 +52,6 @@ function clean_server() {
function configure_server() {
node_select_version_and_npm_ci
# See https://github.com/bubenshchykov/ngrok/issues/254, https://github.com/bubenshchykov/ngrok/pull/255
# TODO: this is horrible; is there a way we can avoid this?
rm -f "$KEYMAN_ROOT"/node_modules/ngrok/bin/ngrok
rm -f "$KEYMAN_ROOT"/node_modules/ngrok/bin/ngrok.exe
}
function build_addins() {
@ -130,9 +126,6 @@ function installer_server() {
pushd "$PRODBUILDTEMP"
npm install --omit=dev --omit=optional
# See https://github.com/bubenshchykov/ngrok/issues/254, https://github.com/bubenshchykov/ngrok/pull/255
rm -f node_modules/ngrok/bin/ngrok
rm -f node_modules/ngrok/bin/ngrok.exe
popd
# Dependencies are required in build/ but we need to copy them in manually

View file

@ -10,11 +10,11 @@
"license": "MIT",
"dependencies": {
"@keymanapp/developer-utils": "*",
"@ngrok/ngrok": "^1.7.0",
"@sentry/node": "^7.57.0",
"chalk": "^4.1.2",
"express": "^4.20.0",
"multer": "^2.0.2",
"ngrok": "^5.0.0-beta.2",
"multer": "^2.1.1",
"open": "^8.4.0",
"restructure": "^3.0.1",
"sax": ">=0.6.0",
@ -29,9 +29,7 @@
"@keymanapp/keyman-version": "*",
"@keymanapp/resources-gosh": "*",
"@types/express": "^4.17.13",
"@types/mocha": "^9.1.0",
"@types/multer": "^1.4.12",
"@types/node": "^20.4.1",
"@types/ws": "^8.2.2",
"copyfiles": "^2.4.1",
"node-gyp": "^10.2.0",

View file

@ -8,7 +8,6 @@ export class Configuration {
public readonly lockFilename: string;
public readonly pidFilename: string;
public readonly configFilename: string;
public readonly ngrokBinPath: string;
/* Configuration values - set in config.json by TIKE */
@ -17,7 +16,6 @@ export class Configuration {
/* ngrok Configuration */
public readonly useNgrok: boolean;
public readonly ngrokControlPort: number;
public readonly ngrokToken: string;
public readonly ngrokVisible: boolean;
@ -42,8 +40,6 @@ export class Configuration {
// ngrok configuration
this.useNgrok = cfg?.useNgrok ?? false;
this.ngrokBinPath = this.appDataPath + 'bin/';
this.ngrokControlPort = cfg?.ngrokControlPort ?? 8009;
this.ngrokToken = cfg?.ngrokToken ?? '';
this.ngrokVisible = cfg?.ngrokVisible ?? false;
}

View file

@ -1,5 +1,4 @@
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import chalk from 'chalk';
import express from 'express';
@ -77,7 +76,7 @@ export async function run() {
setupRoutes(app, upload, wsServer, environment);
/* Start the server */
/* Start the web server */
let server = null;
try {
@ -87,6 +86,8 @@ export async function run() {
// TODO handle and cleanup EADDRINUSE, throw anything else
}
/* Attach the web socket server */
server.on('upgrade', (request, socket, head) => {
wsServer.handleUpgrade(request, socket, head, socket => {
wsServer.emit('connection', socket, request);
@ -96,45 +97,66 @@ export async function run() {
/* Launch ngrok if enabled */
configuration.ngrokEndpoint = '';
if(configuration.useNgrok) {
await startNGrok();
}
if(configuration.useNgrok
&& os.platform() == 'win32'
&& fs.existsSync(path.join(configuration.ngrokBinPath, 'ngrok.exe'))
) {
const ngrok: any = await import('ngrok');
(async function() {
configuration.ngrokEndpoint = await ngrok.connect({
proto: 'http',
addr: configuration.port,
authtoken: configuration.ngrokToken,
binPath: () => configuration.ngrokBinPath,
onLogEvent: (msg: string) => {
if(options.ngrokLog) {
console.log(chalk.cyan(('\n'+msg).split('\n').join('\n[ngrok] ').trim()));
}
},
onStatusChange: (state: string) => {
if(state == 'connected') {
setTimeout(async () => {
const api = ngrok.getApi();
const tunnels = await api.listTunnels();
configuration.ngrokEndpoint = tunnels.tunnels[0]?.public_url ?? '';
console.log(chalk.blueBright('ngrok tunnel established at %s'), configuration.ngrokEndpoint);
}, 1000);
} else if(state == 'closed') {
configuration.ngrokEndpoint = '';
console.log(chalk.blueBright('ngrok tunnel closed'));
}
/* Load the tray icon */
tray.start(configuration.port, configuration.ngrokEndpoint);
}
async function loadNGrok() {
try {
return await import('@ngrok/ngrok');
} catch(e) {
// ngrok can fail import if it cannot load its dependent binary library,
// either because it is missing or because it has missing dependencies
// itself. But we don't want to crash the server if that happens
console.error(e);
return null;
}
}
async function startNGrok() {
console.log('Attempting to start ngrok');
const ngrok = await loadNGrok();
if(!ngrok) {
return false;
}
try {
let started = false;
const listener = await ngrok.forward({
proto: 'http',
addr: configuration.port,
authtoken: configuration.ngrokToken,
onLogEvent: (msg: string) => {
if(options.ngrokLog) {
console.log(chalk.cyan(('\n'+msg).split('\n').join('\n[ngrok] ').trim()));
}
});
console.log(chalk.blueBright('ngrok tunnel initially established at %s'), configuration.ngrokEndpoint);
tray.start(configuration.port, configuration.ngrokEndpoint);
})();
}
else {
/* Load the tray icon */
tray.start(configuration.port, configuration.ngrokEndpoint);
},
onStatusChange: (state: string) => {
if(state == 'connected' && started) {
// We only announce reconnection after initial start
configuration.ngrokEndpoint = listener.url() ?? '';
console.log(chalk.blueBright('ngrok tunnel reconnected at %s'), configuration.ngrokEndpoint);
} else if(state == 'closed') {
configuration.ngrokEndpoint = '';
console.log(chalk.blueBright('ngrok tunnel closed'));
}
}
});
started = true;
configuration.ngrokEndpoint = listener.url();
console.log(chalk.blueBright('ngrok tunnel established at %s'), configuration.ngrokEndpoint);
} catch(e) {
configuration.ngrokEndpoint = '';
console.error(chalk.red('ngrok tunnel failed to connect with an error: %s'), e);
return false;
}
return true;
}
function getRunningInstancePid(pidFilename: string) {

View file

@ -1 +0,0 @@
declare module 'ngrok';

View file

@ -18,25 +18,6 @@
"node-gyp": "^12.1.0"
}
},
"node_modules/@isaacs/balanced-match": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
"integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
"engines": {
"node": "20 || >=22"
}
},
"node_modules/@isaacs/brace-expansion": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz",
"integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==",
"dependencies": {
"@isaacs/balanced-match": "^4.0.1"
},
"engines": {
"node": "20 || >=22"
}
},
"node_modules/@isaacs/fs-minipass": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
@ -90,6 +71,14 @@
"node": ">= 14"
}
},
"node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@ -98,6 +87,17 @@
"file-uri-to-path": "1.0.0"
}
},
"node_modules/brace-expansion": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
"dependencies": {
"balanced-match": "^4.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/cacache": {
"version": "20.0.3",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.3.tgz",
@ -318,14 +318,14 @@
}
},
"node_modules/minimatch": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"dependencies": {
"@isaacs/brace-expansion": "^5.0.0"
"brace-expansion": "^5.0.2"
},
"engines": {
"node": "20 || >=22"
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@ -642,9 +642,9 @@
}
},
"node_modules/tar": {
"version": "7.5.7",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz",
"integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==",
"version": "7.5.10",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.10.tgz",
"integrity": "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==",
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
"chownr": "^3.0.0",
@ -717,19 +717,6 @@
}
},
"dependencies": {
"@isaacs/balanced-match": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
"integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="
},
"@isaacs/brace-expansion": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz",
"integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==",
"requires": {
"@isaacs/balanced-match": "^4.0.1"
}
},
"@isaacs/fs-minipass": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
@ -768,6 +755,11 @@
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="
},
"balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@ -776,6 +768,14 @@
"file-uri-to-path": "1.0.0"
}
},
"brace-expansion": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
"requires": {
"balanced-match": "^4.0.2"
}
},
"cacache": {
"version": "20.0.3",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.3.tgz",
@ -936,11 +936,11 @@
}
},
"minimatch": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"requires": {
"@isaacs/brace-expansion": "^5.0.0"
"brace-expansion": "^5.0.2"
}
},
"minipass": {
@ -1166,9 +1166,9 @@
}
},
"tar": {
"version": "7.5.7",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz",
"integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==",
"version": "7.5.10",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.10.tgz",
"integrity": "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==",
"requires": {
"@isaacs/fs-minipass": "^4.0.0",
"chownr": "^3.0.0",

View file

@ -55,11 +55,13 @@ type
InstallSize: Integer;
end;
TInstallUILevel = (uiSilent, uiProgressOnly, uiFull);
TfrmRun = class(TSetupForm)
public
procedure cmdExitClick(Sender: TObject);
procedure lblTitleClick(Sender: TObject);
procedure cmdInstallKeymanDesktopClick(Sender: TObject);
procedure cmdInstallKeymanDeveloperClick(Sender: TObject);
procedure FormCreate(Sender: TObject); override;
procedure FormShow(Sender: TObject); override;
@ -71,17 +73,13 @@ type
FDownloadFilename: WideString;
FDownloadURL: WideString;
FNewVersion, FInstalledVersion, FInstallerVersion: TMSIInfo;
//FInstalledVersion: WideString;
FContinueSetup: Boolean;
FWhiteBrush: THandle;
FErrorLog: TStream;
FPromptForReboot: Boolean; // I3355 // I3500
FSilent: Boolean;
FUILevel: TInstallUILevel;
FOfflineByRequest: Boolean;
FOnline: Boolean;
//FInstalledProductCode: WideString;
//FInstallerProductCode: WideString;
//FNewProductCode: WideString;
procedure LogError(const msg: WideString; ShowDialogIfNotSilent: Boolean = True);
@ -107,7 +105,7 @@ type
function DialogID: Integer; override;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function DoInstall(Silent, PromptForReboot: Boolean): Boolean; // I3355 // I3500
function DoInstall(UILevel: TInstallUILevel; PromptForReboot: Boolean): Boolean; // I3355 // I3500
property ContinueSetup: Boolean read FContinueSetup write FContinueSetup;
property Offline: Boolean read FOfflineByRequest write SetOfflineByRequest;
end;
@ -225,7 +223,7 @@ end;
function TfrmRun.InstallNewVersion: Boolean;
begin
Result := False;
if not FSilent and (MessageDlgW(FInstallInfo.Text(ssQueryUpdateVersion, [FNewVersion.InstallSize div 1024, FNewVersion.Version]), // I2680
if (FUILevel = uiFull) and (MessageDlgW(FInstallInfo.Text(ssQueryUpdateVersion, [FNewVersion.InstallSize div 1024, FNewVersion.Version]), // I2680
mtConfirmation, mbOkCancel, 0) = mrCancel) then
Exit;
@ -320,12 +318,12 @@ end;
procedure TfrmRun.WMUserFormShown(var Message: TMessage);
begin
if FContinueSetup then
DoInstall(False, True); // I3355 // I3500
DoInstall(uiFull, True); // I3355 // I3500
end;
procedure TfrmRun.cmdInstallKeymanDesktopClick(Sender: TObject);
procedure TfrmRun.cmdInstallKeymanDeveloperClick(Sender: TObject);
begin
if DoInstall(False, True) then // I1901 and I2680.4 // I3355 // I3500
if DoInstall(uiFull, True) then // I1901 and I2680.4 // I3355 // I3500
ModalResult := mrOk;
end;
@ -392,11 +390,10 @@ begin
end;
end;
function TfrmRun.DoInstall(Silent, PromptForReboot: Boolean): Boolean; // I1901 // I3355 // I3500
function TfrmRun.DoInstall(UILevel: TInstallUILevel; PromptForReboot: Boolean): Boolean; // I1901 // I3355 // I3500
begin
FPromptForReboot := PromptForReboot; // I3355 // I3500
FSilent := Silent;
FUILevel := UILevel;
SetCursor(LoadCursor(0, IDC_WAIT));
try
@ -671,8 +668,10 @@ begin
FCacheFileName := CacheMSIFile(ExtPath + FInstallInfo.MSIFileName); // I3310
if FSilent then
if FUILevel = uiSilent then
MsiSetInternalUI(INSTALLUILEVEL_NONE, nil)
else if FUILevel = uiProgressOnly then
MsiSetInternalUI(INSTALLUILEVEL_BASIC, nil)
else
MsiSetInternalUI(INSTALLUILEVEL_FULL, nil);
res := MsiInstallProductW(PWideChar(FCacheFileName), PWideChar('DISABLEFINALOPTIONS=1 '+ReinstallMode));
@ -694,16 +693,13 @@ begin
end;
end;
if not FSilent then
if FUILevel = uiFull then
MsiSetInternalUI(INSTALLUILEVEL_FULL, nil);
if FNewVersion.Filename <> '' then
begin
//Run the setup app silently // I2680.5
TUtilExecute.WaitForProcess('"'+ExtPath + FNewVersion.FileName+'" -s -o', ExtPath, SW_SHOW, WaitFor); // I3349
//if not FSilent then
// MsiSetInternalUI(INSTALLUILEVEL_BASIC, nil);
//CheckMSIResult(MsiApplyPatchW(PWideChar(ExtPath + FNewVersion.Filename), nil, INSTALLTYPE_DEFAULT, ''));
end;
end;
@ -712,7 +708,7 @@ function TfrmRun.ProcessCommand(ID, NotificationCode: Integer;
begin
Result := True;
case ID of
IDOK: cmdInstallKeymanDesktopClick(Self);
IDOK: cmdInstallKeymanDeveloperClick(Self);
IDCANCEL: cmdExitClick(Self);
else Result := False;
end;
@ -727,7 +723,7 @@ procedure TfrmRun.LogError(const msg: WideString; ShowDialogIfNotSilent: Boolean
const
nl: WideString = #13#10;
begin
if not FSilent and ShowDialogIfNotSilent then
if (FUILevel = uiFull) and ShowDialogIfNotSilent then
ShowMessageW(msg);
if not Assigned(FErrorLog) then
begin

View file

@ -1,18 +1,18 @@
(*
Name: bootstrapmain
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Documentation:
Description:
Create Date: 4 Jun 2007
Modified Date: 12 Aug 2014
Authors: mcdurdin
Related Files:
Dependencies:
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
Bugs:
Todo:
Notes:
History: 04 Jun 2007 - mcdurdin - Initial version
05 Jun 2007 - mcdurdin - I817 - Fix -x option
19 Jun 2007 - mcdurdin - I817 - Translate to Unicode and remove Forms dependence
@ -123,12 +123,12 @@ begin
DeletePath(TempPath); // I3310
end;
procedure ProcessCommandLine(var FPromptForReboot, FSilent, FOffline, FExtractOnly, FContinueSetup: Boolean; var FExtractPath: WideString); // I3355 // I3500
procedure ProcessCommandLine(var FUILevel: TInstallUILevel; var FPromptForReboot, FOffline, FExtractOnly, FContinueSetup: Boolean; var FExtractPath: WideString); // I3355 // I3500
var
i: Integer;
begin
FPromptForReboot := True; // I3355 // I3500
FSilent := False;
FUILevel := uiFull;
FOffline := False;
FExtractOnly := False;
FContinueSetup := False;
@ -139,13 +139,13 @@ begin
FContinueSetup := True
else if WideSameText(ParamStr(i), '-au') then // I2849
begin
FSilent := True;
FUILevel := uiProgressOnly;
FOffline := True;
end
else if WideSameText(ParamStr(i), '-s') then
begin
FPromptForReboot := False; // I3355 // I3500
FSilent := True;
FUILevel := uiSilent;
end
else if WideSameText(ParamStr(i), '-o') then
FOffline := True
@ -169,7 +169,7 @@ var
FExtractOnly: Boolean;
FContinueSetup: Boolean;
FPromptForReboot: Boolean; // I3355 // I3500
FSilent: Boolean;
FUILevel: TInstallUILevel;
FOffline: Boolean;
FExtractOnly_Path: WideString;
BEGIN
@ -193,11 +193,11 @@ BEGIN
{ Display the dialog }
//Cancel_From_InfoWin := False;
ProcessCommandLine(FPromptForReboot, FSilent, FOffline, FExtractOnly, FContinueSetup, FExtractOnly_Path); // I3355 // I3500
ProcessCommandLine(FUILevel, FPromptForReboot, FOffline, FExtractOnly, FContinueSetup, FExtractOnly_Path); // I3355 // I3500
if FExtractOnly then
begin
DoExtractOnly(FSilent, FExtractOnly_Path);
DoExtractOnly(FUILevel = uiSilent, FExtractOnly_Path);
Exit;
end;
@ -224,8 +224,8 @@ BEGIN
try
ContinueSetup := FContinueSetup;
Offline := FOffline;
if FSilent
then DoInstall(True, FPromptForReboot) // I3355 // I3500
if FUILevel <> uiFull
then DoInstall(FUILevel, FPromptForReboot) // I3355 // I3500
else ShowModal;
finally
Free;

View file

@ -1,92 +1,94 @@
inherited frmLdmlKeyboardDebug: TfrmLdmlKeyboardDebug
Left = 85
Top = 229
BorderIcons = [biSystemMenu]
BorderStyle = bsSizeToolWin
Caption = 'Debug'
ClientHeight = 132
ClientWidth = 755
Font.Name = 'MS Sans Serif'
FormStyle = fsStayOnTop
KeyPreview = True
Position = poDefault
OnClose = FormClose
OnDestroy = FormDestroy
OnResize = FormResize
ExplicitWidth = 771
ExplicitHeight = 171
PixelsPerInch = 96
TextHeight = 13
object memo: TKeymanDeveloperDebuggerMemo
Left = 0
Top = 0
Width = 755
Height = 73
Cursor = crIBeam
Align = alClient
BorderStyle = bsNone
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Arial'
Font.Style = []
ParentFont = False
PlainText = True
PopupMenu = mnuPopup
ScrollBars = ssVertical
TabOrder = 0
OnChange = memoChange
OnClick = memoClick
OnEnter = memoGotFocus
OnKeyUp = memoKeyUp
OnMessage = memoMessage
IsDebugging = False
end
object sgChars: TStringGrid
Left = 0
Top = 73
Width = 755
Height = 59
Align = alBottom
ColCount = 1
DefaultDrawing = False
FixedCols = 0
RowCount = 2
FixedRows = 0
ScrollBars = ssNone
TabOrder = 1
OnDrawCell = sgCharsDrawCell
ColWidths = (
64)
RowHeights = (
24
24)
end
object mnuPopup: TPopupMenu
Images = frmKeymanDeveloper.lstImages
Left = 228
Top = 73
object mnuPopupFont: TMenuItem
Action = modActionsDebugger.actDebugViewFont
end
object mnuPopupResetToQuotedFont: TMenuItem
Action = modActionsDebugger.actDebugViewDefaultFont
end
object N1: TMenuItem
Caption = '-'
end
object mnuCut: TMenuItem
Action = modActionsMain.actEditCut
end
object mnuCopy: TMenuItem
Action = modActionsMain.actEditCopy
end
object mnuPaste: TMenuItem
Action = modActionsMain.actEditPaste
end
end
object appEvents: TApplicationEvents
Left = 368
Top = 72
end
end
inherited frmLdmlKeyboardDebug: TfrmLdmlKeyboardDebug
Left = 85
Top = 229
BorderIcons = [biSystemMenu]
BorderStyle = bsSizeToolWin
Caption = 'Debug'
ClientHeight = 132
ClientWidth = 755
Font.Name = 'MS Sans Serif'
FormStyle = fsStayOnTop
KeyPreview = True
Position = poDefault
OnClose = FormClose
OnDestroy = FormDestroy
OnResize = FormResize
ExplicitWidth = 771
ExplicitHeight = 171
PixelsPerInch = 96
TextHeight = 13
object memo: TKeymanDeveloperDebuggerMemo
Left = 0
Top = 0
Width = 755
Height = 73
Cursor = crIBeam
Align = alClient
BorderStyle = bsNone
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Arial'
Font.Style = []
ParentFont = False
PlainText = True
PopupMenu = mnuPopup
ScrollBars = ssVertical
TabOrder = 0
OnChange = memoChange
OnClick = memoClick
OnDblClick = memoDblClick
OnEnter = memoGotFocus
OnKeyUp = memoKeyUp
OnSelectionChange = memoSelMove
OnMessage = memoMessage
IsDebugging = False
end
object sgChars: TStringGrid
Left = 0
Top = 73
Width = 755
Height = 59
Align = alBottom
ColCount = 1
DefaultDrawing = False
FixedCols = 0
RowCount = 2
FixedRows = 0
ScrollBars = ssNone
TabOrder = 1
OnDrawCell = sgCharsDrawCell
ColWidths = (
64)
RowHeights = (
24
24)
end
object mnuPopup: TPopupMenu
Images = frmKeymanDeveloper.lstImages
Left = 228
Top = 73
object mnuPopupFont: TMenuItem
Action = modActionsDebugger.actDebugViewFont
end
object mnuPopupResetToQuotedFont: TMenuItem
Action = modActionsDebugger.actDebugViewDefaultFont
end
object N1: TMenuItem
Caption = '-'
end
object mnuCut: TMenuItem
Action = modActionsMain.actEditCut
end
object mnuCopy: TMenuItem
Action = modActionsMain.actEditCopy
end
object mnuPaste: TMenuItem
Action = modActionsMain.actEditPaste
end
end
object appEvents: TApplicationEvents
Left = 368
Top = 72
end
end

View file

@ -74,6 +74,7 @@ type
procedure memoClick(Sender: TObject); // I4808
procedure memoKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure FormResize(Sender: TObject);
procedure memoDblClick(Sender: TObject);
private
FFileName: string;
debugkeyboard: TDebugKeyboard;
@ -108,6 +109,7 @@ type
function SetKeyEventContext: Boolean;
function HandleMemoKeydown(var Message: TMessage): Boolean;
procedure Run(vkey: Word);
procedure UpdateMemoSelection;
protected
function GetHelpTopic: string; override;
@ -206,13 +208,13 @@ end;
procedure TfrmLdmlKeyboardDebug.memoGotFocus(Sender: TObject);
begin
memoSelMove(memo);
UpdateMemoSelection;
end;
procedure TfrmLdmlKeyboardDebug.memoKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
memoSelMove(memo);
UpdateMemoSelection;
end;
function TfrmLdmlKeyboardDebug.HandleMemoKeydown(var Message: TMessage): Boolean;
@ -391,7 +393,7 @@ procedure TfrmLdmlKeyboardDebug.Run(vkey: Word);
dk: TDeadKeyInfo;
begin
Result.Selection := memo.Selection;
Result.Length := Length(memo.Text);
Result.Length := Length(memo.GetTextCR);
for dk in FDeadkeys do
begin
if dk.Position >= Result.Selection.Start
@ -406,7 +408,7 @@ procedure TfrmLdmlKeyboardDebug.Run(vkey: Word);
L: Integer;
s: string;
begin
s := memo.Text;
s := memo.GetTextCR;
L := Length(s);
for dk in FDeadkeys do
if dk.SavedPosition < 0 then
@ -430,10 +432,6 @@ procedure TfrmLdmlKeyboardDebug.Run(vkey: Word);
if GetKeyState(VK_CONTROL) >= 0 then
begin
if (vk = VK_RETURN) and (GetKeyState(VK_MENU) >= 0) then
begin
memo.SelText := #13#10;
end;
Exit;
end;
@ -591,13 +589,13 @@ begin
RealignMemoSelectionState(state);
selection := memo.Selection;
end;
lhs := Copy(memo.Text, 1, selection.Start);
lhs := Copy(memo.GetTextCR, 1, selection.Start);
end
else
lhs := '';
context := Copy(memo.Text, lhs.Length + 1, selection.Start - lhs.Length);
rhs := Copy(memo.Text, lhs.Length + context.Length + 1, MaxInt);
context := Copy(memo.GetTextCR, lhs.Length + 1, selection.Start - lhs.Length);
rhs := Copy(memo.GetTextCR, lhs.Length + context.Length + 1, MaxInt);
// Reinsert the context
@ -635,7 +633,7 @@ begin
SendMessage(memo.Handle, (WM_USER + 97) {EM_SETTEXTEX}, NativeUint(@ste), NativeUInt(PChar(str)));
selection.Start := lhs.Length + output.Length;
selection.Finish := selection.Start;
selection.Anchor := selection.Start;
selection.Caret := selection.Start;
memo.Selection := selection;
finally
memo.Lines.EndUpdate;
@ -820,22 +818,32 @@ end;
procedure TfrmLdmlKeyboardDebug.memoChange(Sender: TObject);
begin
UpdateDeadkeys;
memoSelMove(memo);
UpdateMemoSelection;
end;
procedure TfrmLdmlKeyboardDebug.memoClick(Sender: TObject);
begin
memoSelMove(memo);
UpdateMemoSelection;
end;
procedure TfrmLdmlKeyboardDebug.memoDblClick(Sender: TObject);
begin
UpdateMemoSelection;
end;
procedure TfrmLdmlKeyboardDebug.memoSelMove(Sender: TObject);
begin
UpdateMemoSelection;
end;
procedure TfrmLdmlKeyboardDebug.UpdateMemoSelection;
begin
if memo.Focused then
begin
frmKeymanDeveloper.barStatus.Panels[0].Text := 'Debugger Active';
end;
if not memo.ReadOnly and not memo.SelectionChanging then
if not memo.ReadOnly then
begin
UpdateCharacterGrid; // I4808
end;
@ -844,20 +852,22 @@ end;
procedure TfrmLdmlKeyboardDebug.UpdateCharacterGrid; // I4808
var
start, len: Integer;
text: string;
begin
if csDestroying in ComponentState then
Exit;
start := memo.SelStart;
len := memo.SelLength;
if start + len > Length(memo.Text) then
text := memo.GetTextCR;
if start + len > text.Length then
begin
// RichEdit has a virtual final character, which is selected when
// pressing Ctrl+A, etc.
len := Length(memo.Text) - start;
len := text.Length - start;
end;
TCharacterGridRenderer.Fill(sgChars, memo.Text, FDeadkeys, start, len,
memo.Selection.Anchor, True);
TCharacterGridRenderer.Fill(sgChars, text, FDeadkeys, start, len,
memo.Selection.Caret, True);
TCharacterGridRenderer.Size(sgChars, memo.Font);
end;

View file

@ -1,93 +1,95 @@
inherited frmDebug: TfrmDebug
Left = 85
Top = 229
BorderIcons = [biSystemMenu]
BorderStyle = bsSizeToolWin
Caption = 'Debug'
ClientHeight = 132
ClientWidth = 755
Font.Name = 'MS Sans Serif'
FormStyle = fsStayOnTop
KeyPreview = True
Position = poDefault
OnClose = FormClose
OnDestroy = FormDestroy
OnResize = FormResize
ExplicitWidth = 771
ExplicitHeight = 171
PixelsPerInch = 96
TextHeight = 13
object memo: TKeymanDeveloperDebuggerMemo
Left = 0
Top = 0
Width = 755
Height = 73
Cursor = crIBeam
Align = alClient
BorderStyle = bsNone
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Arial'
Font.Style = []
ParentFont = False
PlainText = True
PopupMenu = mnuPopup
ScrollBars = ssVertical
TabOrder = 0
OnChange = memoChange
OnClick = memoClick
OnEnter = memoGotFocus
OnExit = memoLostFocus
OnKeyUp = memoKeyUp
OnMessage = memoMessage
IsDebugging = False
end
object sgChars: TStringGrid
Left = 0
Top = 73
Width = 755
Height = 59
Align = alBottom
ColCount = 1
DefaultDrawing = False
FixedCols = 0
RowCount = 2
FixedRows = 0
ScrollBars = ssNone
TabOrder = 1
OnDrawCell = sgCharsDrawCell
ColWidths = (
64)
RowHeights = (
24
24)
end
object mnuPopup: TPopupMenu
Images = frmKeymanDeveloper.lstImages
Left = 228
Top = 73
object mnuPopupFont: TMenuItem
Action = modActionsDebugger.actDebugViewFont
end
object mnuPopupResetToQuotedFont: TMenuItem
Action = modActionsDebugger.actDebugViewDefaultFont
end
object N1: TMenuItem
Caption = '-'
end
object mnuCut: TMenuItem
Action = modActionsMain.actEditCut
end
object mnuCopy: TMenuItem
Action = modActionsMain.actEditCopy
end
object mnuPaste: TMenuItem
Action = modActionsMain.actEditPaste
end
end
object appEvents: TApplicationEvents
Left = 368
Top = 72
end
end
inherited frmDebug: TfrmDebug
Left = 85
Top = 229
BorderIcons = [biSystemMenu]
BorderStyle = bsSizeToolWin
Caption = 'Debug'
ClientHeight = 132
ClientWidth = 755
Font.Name = 'MS Sans Serif'
FormStyle = fsStayOnTop
KeyPreview = True
Position = poDefault
OnClose = FormClose
OnDestroy = FormDestroy
OnResize = FormResize
ExplicitWidth = 771
ExplicitHeight = 171
PixelsPerInch = 96
TextHeight = 13
object memo: TKeymanDeveloperDebuggerMemo
Left = 0
Top = 0
Width = 755
Height = 73
Cursor = crIBeam
Align = alClient
BorderStyle = bsNone
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Arial'
Font.Style = []
ParentFont = False
PlainText = True
PopupMenu = mnuPopup
ScrollBars = ssVertical
TabOrder = 0
OnChange = memoChange
OnClick = memoClick
OnDblClick = memoDblClick
OnEnter = memoGotFocus
OnExit = memoLostFocus
OnKeyUp = memoKeyUp
OnSelectionChange = memoSelMove
OnMessage = memoMessage
IsDebugging = False
end
object sgChars: TStringGrid
Left = 0
Top = 73
Width = 755
Height = 59
Align = alBottom
ColCount = 1
DefaultDrawing = False
FixedCols = 0
RowCount = 2
FixedRows = 0
ScrollBars = ssNone
TabOrder = 1
OnDrawCell = sgCharsDrawCell
ColWidths = (
64)
RowHeights = (
24
24)
end
object mnuPopup: TPopupMenu
Images = frmKeymanDeveloper.lstImages
Left = 228
Top = 73
object mnuPopupFont: TMenuItem
Action = modActionsDebugger.actDebugViewFont
end
object mnuPopupResetToQuotedFont: TMenuItem
Action = modActionsDebugger.actDebugViewDefaultFont
end
object N1: TMenuItem
Caption = '-'
end
object mnuCut: TMenuItem
Action = modActionsMain.actEditCut
end
object mnuCopy: TMenuItem
Action = modActionsMain.actEditCopy
end
object mnuPaste: TMenuItem
Action = modActionsMain.actEditPaste
end
end
object appEvents: TApplicationEvents
Left = 368
Top = 72
end
end

View file

@ -86,6 +86,7 @@ type
procedure memoClick(Sender: TObject); // I4808
procedure memoKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure FormResize(Sender: TObject);
procedure memoDblClick(Sender: TObject);
private
FDebugVisible: Boolean;
@ -118,6 +119,7 @@ type
function GetStatusText: string;
procedure SetStatusText(Value: string);
procedure UpdateDebugStatusForm; // I4809
procedure UpdateMemoSelection;
private
FDebugCore: TDebugCore;
@ -147,6 +149,7 @@ type
procedure SetCurrentEvent(Value: Integer);
procedure FinishBatch;
procedure StartBatch;
procedure RecordBreadcrumb(const message: string);
protected
function GetHelpTopic: string; override;
@ -306,13 +309,13 @@ begin
if UIStatus = duiReadyForInput then
UIStatus := duiFocusedForInput;
memoSelMove(memo);
UpdateMemoSelection;
end;
procedure TfrmDebug.memoKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
memoSelMove(memo);
UpdateMemoSelection;
end;
procedure TfrmDebug.memoLostFocus(Sender: TObject);
@ -463,9 +466,8 @@ begin
modifier := modifier and not KM_CORE_MODIFIER_LCTRL;
end;
TKeymanSentryClient.Instance.Breadcrumb('default',
Format('ProcessKeyEvent: vk=%x mod=%x scan=%x beforeText="%s" beforeSelection=%s',[vkey, modifier, scan, Copy(memo.GetTextCR, 1, 256), memo.Selection.ToString]),
'debugger');
RecordBreadcrumb(
Format('ProcessKeyEvent: vk=%x mod=%x scan=%x beforeText="%s" beforeSelection=%s savedSelection=%s',[vkey, modifier, scan, Copy(memo.GetTextCR, 1, 256), memo.Selection.ToString, FSavedSelection.ToString]));
if not SetKeyEventContext then
Exit(False);
@ -601,12 +603,17 @@ end;
{ Stores }
procedure TfrmDebug.RecordBreadcrumb(const message: string);
begin
// TODO(v20): eliminate breadcrumbs once underlying bug #11706 is confirmed fixed
TKeymanSentryClient.Instance.Breadcrumb('default', message, 'debugger');
// OutputDebugString(PChar(message));
end;
procedure TfrmDebug.ExecuteEvent(n: Integer);
begin
TKeymanSentryClient.Instance.Breadcrumb('default',
Format('ExecuteEvent(%d/%d): [%s]; b:%s; s:%s',
[n, FEvents.Count, FEvents[n].ToString, memo.Selection.ToString, FSavedSelection.ToString]),
'debugger');
RecordBreadcrumb(Format('ExecuteEvent(%d/%d): [%s] beforeSelection=%s savedSelection=%s',
[n, FEvents.Count, FEvents[n].ToString, memo.Selection.ToString, FSavedSelection.ToString]));
memo.ReadOnly := False;
memo.Selection := FSavedSelection;
@ -1323,22 +1330,32 @@ end;
procedure TfrmDebug.memoChange(Sender: TObject);
begin
UpdateDeadkeys;
memoSelMove(memo);
UpdateMemoSelection;
end;
procedure TfrmDebug.memoClick(Sender: TObject);
begin
memoSelMove(memo);
UpdateMemoSelection;
end;
procedure TfrmDebug.memoDblClick(Sender: TObject);
begin
UpdateMemoSelection;
end;
procedure TfrmDebug.memoSelMove(Sender: TObject);
begin
UpdateMemoSelection;
end;
procedure TfrmDebug.UpdateMemoSelection;
begin
if memo.Focused then
begin
frmKeymanDeveloper.barStatus.Panels[0].Text := 'Debugger Active';
end;
if not memo.ReadOnly and not memo.SelectionChanging then
if not memo.ReadOnly then
begin
FSavedSelection := memo.Selection;
UpdateCharacterGrid; // I4808
@ -1363,7 +1380,7 @@ begin
len := t.Length - start;
end;
TCharacterGridRenderer.Fill(sgChars, t, FDeadkeys, start, len, memo.Selection.Anchor);
TCharacterGridRenderer.Fill(sgChars, t, FDeadkeys, start, len, memo.Selection.Caret);
TCharacterGridRenderer.Size(sgChars, memo.Font);
end;

View file

@ -22,7 +22,7 @@ type
class procedure Fill(grid: TStringGrid; const text: string;
deadkeys: TDebugDeadkeyInfoList;
SelStart, SelLength, SelAnchor: Integer;
SelStart, SelLength, Caret: Integer;
DeadkeysAreCalledMarkers: Boolean = False); static;
class procedure Render(grid: TStringGrid; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState; CharFont: TFont); static;
@ -37,7 +37,7 @@ uses
class procedure TCharacterGridRenderer.Fill(grid: TStringGrid;
const text: string; deadkeys: TDebugDeadkeyInfoList;
SelStart, SelLength, SelAnchor: Integer;
SelStart, SelLength, Caret: Integer;
DeadkeysAreCalledMarkers: Boolean);
type
TCellType = (ctChar, ctDeadkey);
@ -171,10 +171,10 @@ begin
X := 0;
FillGrid(Before, 0, X);
if SelAnchor = SelStart then
if Caret = SelStart then
FillGridCursor(X);
FillGrid(Selection, CELL_SELECTED, X);
if SelAnchor <> SelStart then
if Caret <> SelStart then
FillGridCursor(X);
FillGrid(After, 0, X);
end;

Some files were not shown because too many files have changed in this diff Show more