plover 5.0.0.dev2__py3-none-any.whl → 5.0.0.dev3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plover/__init__.py +8 -7
- plover/__main__.py +1 -1
- plover/command/set_config.py +7 -4
- plover/config.py +177 -74
- plover/dictionary/base.py +21 -9
- plover/dictionary/helpers.py +6 -4
- plover/dictionary/json_dict.py +10 -12
- plover/dictionary/loading_manager.py +8 -10
- plover/dictionary/rtfcre_dict.py +46 -43
- plover/dictionary/rtfcre_parse.py +116 -100
- plover/engine.py +112 -96
- plover/exception.py +4 -1
- plover/formatting.py +198 -140
- plover/gui_none/add_translation.py +17 -16
- plover/gui_none/engine.py +1 -3
- plover/gui_none/main.py +2 -2
- plover/gui_qt/about_dialog.py +17 -17
- plover/gui_qt/add_translation_dialog.py +8 -9
- plover/gui_qt/add_translation_widget.py +66 -63
- plover/gui_qt/config_window.py +274 -154
- plover/gui_qt/console_widget.py +9 -13
- plover/gui_qt/dictionaries_widget.py +140 -119
- plover/gui_qt/dictionary_editor.py +60 -61
- plover/gui_qt/engine.py +3 -3
- plover/gui_qt/info_browser.py +50 -4
- plover/gui_qt/log_qt.py +3 -2
- plover/gui_qt/lookup_dialog.py +9 -9
- plover/gui_qt/machine_options.py +38 -37
- plover/gui_qt/main.py +19 -19
- plover/gui_qt/main_window.py +88 -71
- plover/gui_qt/paper_tape.py +69 -52
- plover/gui_qt/paper_tape_ui.py +0 -3
- plover/gui_qt/plugins_manager.py +47 -43
- plover/gui_qt/resources_rc.py +28 -28
- plover/gui_qt/run_dialog.py +7 -6
- plover/gui_qt/steno_validator.py +2 -3
- plover/gui_qt/suggestions_dialog.py +34 -38
- plover/gui_qt/suggestions_widget.py +22 -16
- plover/gui_qt/tool.py +1 -3
- plover/gui_qt/trayicon.py +19 -19
- plover/gui_qt/utils.py +19 -9
- plover/i18n.py +9 -8
- plover/key_combo.py +130 -130
- plover/log.py +26 -26
- plover/machine/base.py +40 -35
- plover/machine/geminipr.py +7 -7
- plover/machine/keyboard.py +16 -14
- plover/machine/keyboard_capture/__init__.py +0 -1
- plover/machine/keymap.py +24 -20
- plover/machine/passport.py +6 -5
- plover/machine/procat.py +12 -16
- plover/machine/stentura.py +45 -29
- plover/machine/txbolt.py +5 -3
- plover/macro/repeat.py +0 -1
- plover/macro/retro.py +9 -9
- plover/macro/undo.py +4 -3
- plover/meta/attach.py +12 -10
- plover/meta/case.py +2 -1
- plover/meta/conditional.py +3 -3
- plover/meta/currency.py +4 -4
- plover/meta/mode.py +15 -15
- plover/meta/punctuation.py +1 -0
- plover/misc.py +21 -17
- plover/orthography.py +12 -10
- plover/oslayer/__init__.py +7 -5
- plover/oslayer/config.py +16 -15
- plover/oslayer/controller.py +13 -13
- plover/oslayer/linux/i18n.py +2 -1
- plover/oslayer/linux/keyboardcontrol.py +2 -2
- plover/oslayer/linux/keyboardcontrol_uinput.py +48 -3
- plover/oslayer/linux/keyboardcontrol_x11.py +860 -851
- plover/oslayer/linux/log.py +1 -1
- plover/oslayer/linux/log_dbus.py +97 -65
- plover/oslayer/linux/serial.py +2 -2
- plover/oslayer/linux/wmctrl_x11.py +11 -15
- plover/oslayer/osx/keyboardcontrol.py +194 -99
- plover/oslayer/osx/keyboardlayout.py +138 -119
- plover/oslayer/osx/log.py +14 -8
- plover/oslayer/osx/serial.py +1 -1
- plover/oslayer/osx/wmctrl.py +6 -1
- plover/oslayer/windows/keyboardcontrol.py +195 -89
- plover/oslayer/windows/keyboardlayout.py +367 -334
- plover/oslayer/windows/log.py +5 -3
- plover/oslayer/windows/serial.py +4 -5
- plover/oslayer/windows/wmctrl.py +1 -1
- plover/output/__init__.py +1 -2
- plover/output/keyboard.py +15 -15
- plover/plugins_manager/__main__.py +42 -39
- plover/plugins_manager/global_registry.py +2 -5
- plover/plugins_manager/local_registry.py +11 -13
- plover/plugins_manager/package_index.py +12 -18
- plover/plugins_manager/pip_wrapper.py +5 -5
- plover/plugins_manager/plugin_metadata.py +17 -9
- plover/plugins_manager/registry.py +39 -28
- plover/plugins_manager/requests.py +2 -4
- plover/plugins_manager/utils.py +9 -7
- plover/registry.py +28 -26
- plover/resource.py +13 -10
- plover/scripts/dist_main.py +7 -7
- plover/scripts/main.py +57 -34
- plover/scripts/send_command.py +13 -7
- plover/steno.py +16 -9
- plover/steno_dictionary.py +47 -21
- plover/suggestions.py +11 -11
- plover/system/__init__.py +39 -23
- plover/system/english_stenotype.py +231 -229
- plover/translation.py +67 -51
- {plover-5.0.0.dev2.dist-info → plover-5.0.0.dev3.dist-info}/METADATA +1 -1
- plover-5.0.0.dev3.dist-info/RECORD +216 -0
- plover_build_utils/check_requirements.py +5 -6
- plover_build_utils/download.py +10 -8
- plover_build_utils/get_pip.py +10 -5
- plover_build_utils/install_wheels.py +33 -31
- plover_build_utils/pyqt.py +24 -22
- plover_build_utils/setup.py +64 -54
- plover_build_utils/source_less.py +6 -6
- plover_build_utils/testing/blackbox.py +31 -31
- plover_build_utils/testing/dict.py +3 -3
- plover_build_utils/testing/output.py +5 -6
- plover_build_utils/testing/parametrize.py +5 -3
- plover_build_utils/testing/steno_dictionary.py +145 -124
- plover_build_utils/tree.py +21 -19
- plover_build_utils/trim.py +5 -5
- plover_build_utils/zipdir.py +3 -3
- plover-5.0.0.dev2.dist-info/RECORD +0 -216
- {plover-5.0.0.dev2.dist-info → plover-5.0.0.dev3.dist-info}/WHEEL +0 -0
- {plover-5.0.0.dev2.dist-info → plover-5.0.0.dev3.dist-info}/entry_points.txt +0 -0
- {plover-5.0.0.dev2.dist-info → plover-5.0.0.dev3.dist-info}/licenses/LICENSE.txt +0 -0
- {plover-5.0.0.dev2.dist-info → plover-5.0.0.dev3.dist-info}/top_level.txt +0 -0
- {plover-5.0.0.dev2.dist-info → plover-5.0.0.dev3.dist-info}/zip-safe +0 -0
plover/machine/stentura.py
CHANGED
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
# is a connection error.
|
|
7
7
|
# TODO: Address any generic exceptions still left.
|
|
8
8
|
|
|
9
|
-
"""Thread-based monitoring of a stenotype machine using the stentura protocol.
|
|
10
|
-
"""
|
|
9
|
+
"""Thread-based monitoring of a stenotype machine using the stentura protocol."""
|
|
11
10
|
|
|
12
11
|
"""
|
|
13
12
|
The stentura protocol uses packets to communicate with the machine. A
|
|
@@ -157,8 +156,8 @@ def buffer(object, offset=None, size=None):
|
|
|
157
156
|
if offset is None:
|
|
158
157
|
offset = 0
|
|
159
158
|
if size is None:
|
|
160
|
-
size = len(object)-offset
|
|
161
|
-
return memoryview(object)[offset:offset+size]
|
|
159
|
+
size = len(object) - offset
|
|
160
|
+
return memoryview(object)[offset : offset + size]
|
|
162
161
|
|
|
163
162
|
|
|
164
163
|
def _allocate_buffer():
|
|
@@ -167,24 +166,29 @@ def _allocate_buffer():
|
|
|
167
166
|
|
|
168
167
|
class _ProtocolViolationException(Exception):
|
|
169
168
|
"""Something has happened that is doesn't follow the protocol."""
|
|
169
|
+
|
|
170
170
|
pass
|
|
171
171
|
|
|
172
172
|
|
|
173
173
|
class _StopException(Exception):
|
|
174
174
|
"""The thread was asked to stop."""
|
|
175
|
+
|
|
175
176
|
pass
|
|
176
177
|
|
|
177
178
|
|
|
178
179
|
class _TimeoutException(Exception):
|
|
179
180
|
"""An operation has timed out."""
|
|
181
|
+
|
|
180
182
|
pass
|
|
181
183
|
|
|
182
184
|
|
|
183
185
|
class _ConnectionLostException(Exception):
|
|
184
186
|
"""Cannot communicate with the machine."""
|
|
187
|
+
|
|
185
188
|
pass
|
|
186
189
|
|
|
187
190
|
|
|
191
|
+
# fmt: off
|
|
188
192
|
_CRC_TABLE = [
|
|
189
193
|
0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
|
|
190
194
|
0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
|
|
@@ -219,6 +223,7 @@ _CRC_TABLE = [
|
|
|
219
223
|
0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
|
|
220
224
|
0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
|
|
221
225
|
]
|
|
226
|
+
# fmt: on
|
|
222
227
|
|
|
223
228
|
|
|
224
229
|
def _crc(data, offset=None, size=None):
|
|
@@ -250,8 +255,7 @@ def _crc(data, offset=None, size=None):
|
|
|
250
255
|
checksum = 0
|
|
251
256
|
for n in range(offset, offset + size):
|
|
252
257
|
b = data[n]
|
|
253
|
-
checksum =
|
|
254
|
-
((checksum >> 8) & 0xff))
|
|
258
|
+
checksum = _CRC_TABLE[(checksum ^ b) & 0xFF] ^ ((checksum >> 8) & 0xFF)
|
|
255
259
|
return checksum
|
|
256
260
|
|
|
257
261
|
|
|
@@ -265,14 +269,17 @@ def _write_to_buffer(buf, offset, data):
|
|
|
265
269
|
- offset. The offset at which to start writing.
|
|
266
270
|
- data: An iterable containing the data to write.
|
|
267
271
|
"""
|
|
268
|
-
buf[offset:offset+len(data)] = data
|
|
272
|
+
buf[offset : offset + len(data)] = data
|
|
273
|
+
|
|
269
274
|
|
|
270
275
|
# Helper table for parsing strokes of the form:
|
|
271
276
|
# 11^#STKP 11WHRAO* 11EUFRPB 11LGTSDZ
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
277
|
+
# fmt: off
|
|
278
|
+
_STENO_KEY_CHART = ("^", "#", "S-", "T-", "K-", "P-", # Byte #1
|
|
279
|
+
"W-", "H-", "R-", "A-", "O-", "*", # Byte #2
|
|
280
|
+
"-E", "-U", "-F", "-R", "-P", "-B", # Byte #3
|
|
281
|
+
"-L", "-G", "-T", "-S", "-D", "-Z") # Byte #4
|
|
282
|
+
# fmt: on
|
|
276
283
|
|
|
277
284
|
|
|
278
285
|
def _parse_stroke(a, b, c, d):
|
|
@@ -288,10 +295,8 @@ def _parse_stroke(a, b, c, d):
|
|
|
288
295
|
e.g. ['S-', 'A-', '-T']
|
|
289
296
|
|
|
290
297
|
"""
|
|
291
|
-
fullstroke = ((
|
|
292
|
-
|
|
293
|
-
return [_STENO_KEY_CHART[i] for i in range(24)
|
|
294
|
-
if (fullstroke & (1 << (23 - i)))]
|
|
298
|
+
fullstroke = ((a & 0x3F) << 18) | ((b & 0x3F) << 12) | ((c & 0x3F) << 6) | d & 0x3F
|
|
299
|
+
return [_STENO_KEY_CHART[i] for i in range(24) if (fullstroke & (1 << (23 - i)))]
|
|
295
300
|
|
|
296
301
|
|
|
297
302
|
def _parse_strokes(data):
|
|
@@ -309,7 +314,8 @@ def _parse_strokes(data):
|
|
|
309
314
|
strokes = []
|
|
310
315
|
if (len(data) % 4) != 0:
|
|
311
316
|
raise _ProtocolViolationException(
|
|
312
|
-
"Data size is not divisible by 4: %d" % (len(data))
|
|
317
|
+
"Data size is not divisible by 4: %d" % (len(data))
|
|
318
|
+
)
|
|
313
319
|
for b in data:
|
|
314
320
|
if (b & 0b11000000) != 0b11000000:
|
|
315
321
|
raise _ProtocolViolationException("Data is not stroke: 0x%X" % (b))
|
|
@@ -317,6 +323,7 @@ def _parse_strokes(data):
|
|
|
317
323
|
strokes.append(_parse_stroke(a, b, c, d))
|
|
318
324
|
return strokes
|
|
319
325
|
|
|
326
|
+
|
|
320
327
|
# Actions
|
|
321
328
|
_CLOSE = 0x2
|
|
322
329
|
_DELETE = 0x3
|
|
@@ -329,8 +336,8 @@ _RESET = 0x14
|
|
|
329
336
|
_TERM = 0x15
|
|
330
337
|
|
|
331
338
|
# Compiled struct for writing request headers.
|
|
332
|
-
_REQUEST_STRUCT = struct.Struct(
|
|
333
|
-
_SHORT_STRUCT = struct.Struct(
|
|
339
|
+
_REQUEST_STRUCT = struct.Struct("<2B7H")
|
|
340
|
+
_SHORT_STRUCT = struct.Struct("<H")
|
|
334
341
|
|
|
335
342
|
|
|
336
343
|
def _make_request(buf, action, seq, p1=0, p2=0, p3=0, p4=0, p5=0, data=None):
|
|
@@ -351,8 +358,7 @@ def _make_request(buf, action, seq, p1=0, p2=0, p3=0, p4=0, p5=0, data=None):
|
|
|
351
358
|
length = 18
|
|
352
359
|
if data:
|
|
353
360
|
length += len(data) + 2 # +2 for the data CRC.
|
|
354
|
-
_REQUEST_STRUCT.pack_into(buf, 0, 1, seq, length, action,
|
|
355
|
-
p1, p2, p3, p4, p5)
|
|
361
|
+
_REQUEST_STRUCT.pack_into(buf, 0, 1, seq, length, action, p1, p2, p3, p4, p5)
|
|
356
362
|
crc = _crc(buf, 1, 15)
|
|
357
363
|
_SHORT_STRUCT.pack_into(buf, 16, crc)
|
|
358
364
|
if data:
|
|
@@ -460,7 +466,10 @@ def _read_data(port, stop, buf, offset, num_bytes):
|
|
|
460
466
|
_write_to_buffer(buf, offset, read_bytes)
|
|
461
467
|
return len(read_bytes)
|
|
462
468
|
|
|
469
|
+
|
|
463
470
|
MINIMUM_PACKET_LENGTH = 14
|
|
471
|
+
|
|
472
|
+
|
|
464
473
|
def _read_packet(port, stop, buf):
|
|
465
474
|
"""Read a full packet from the port.
|
|
466
475
|
|
|
@@ -487,8 +496,7 @@ def _read_packet(port, stop, buf):
|
|
|
487
496
|
# Packet length should always be at least 14 bytes long
|
|
488
497
|
if packet_length < MINIMUM_PACKET_LENGTH:
|
|
489
498
|
raise _ProtocolViolationException()
|
|
490
|
-
bytes_read += _read_data(port, stop, buf, bytes_read,
|
|
491
|
-
packet_length - bytes_read)
|
|
499
|
+
bytes_read += _read_data(port, stop, buf, bytes_read, packet_length - bytes_read)
|
|
492
500
|
packet = buffer(buf, 0, bytes_read)
|
|
493
501
|
if not _validate_response(packet):
|
|
494
502
|
raise _ProtocolViolationException()
|
|
@@ -548,6 +556,7 @@ def _send_receive(port, stop, packet, buf, max_tries=3):
|
|
|
548
556
|
|
|
549
557
|
class _SequenceCounter:
|
|
550
558
|
"""A mod 256 counter."""
|
|
559
|
+
|
|
551
560
|
def __init__(self, seq=0):
|
|
552
561
|
"""Init a new counter starting at seq."""
|
|
553
562
|
self.seq = seq
|
|
@@ -582,8 +591,10 @@ def _read(port, stop, seq, request_buf, response_buf, stroke_buf, block, byte):
|
|
|
582
591
|
packet = _make_read(request_buf, seq(), block, byte, length=512)
|
|
583
592
|
response = _send_receive(port, stop, packet, response_buf)
|
|
584
593
|
p1 = _SHORT_STRUCT.unpack(response[8:10])[0]
|
|
585
|
-
if not (
|
|
586
|
-
|
|
594
|
+
if not (
|
|
595
|
+
(p1 == 0 and len(response) == 14) # No data.
|
|
596
|
+
or (p1 == len(response) - 16)
|
|
597
|
+
): # Data.
|
|
587
598
|
raise _ProtocolViolationException()
|
|
588
599
|
if p1 == 0:
|
|
589
600
|
return block, byte, buffer(stroke_buf, 0, bytes_read)
|
|
@@ -595,6 +606,7 @@ def _read(port, stop, seq, request_buf, response_buf, stroke_buf, block, byte):
|
|
|
595
606
|
block += 1
|
|
596
607
|
byte -= 512
|
|
597
608
|
|
|
609
|
+
|
|
598
610
|
def _loop(port, stop, callback, ready_callback, timeout=1):
|
|
599
611
|
"""Enter into a loop talking to the machine and returning strokes.
|
|
600
612
|
|
|
@@ -628,15 +640,19 @@ def _loop(port, stop, callback, ready_callback, timeout=1):
|
|
|
628
640
|
request_buf, response_buf = _allocate_buffer(), _allocate_buffer()
|
|
629
641
|
stroke_buf = _allocate_buffer()
|
|
630
642
|
seq = _SequenceCounter()
|
|
631
|
-
request = _make_open(request_buf, seq(), b
|
|
643
|
+
request = _make_open(request_buf, seq(), b"A", b"REALTIME.000")
|
|
632
644
|
# Any checking needed on the response packet?
|
|
633
645
|
_send_receive(port, stop, request, response_buf)
|
|
634
646
|
# Do a full read to get to the current position in the realtime file.
|
|
635
647
|
block, byte = 0, 0
|
|
636
|
-
block, byte, _ = _read(
|
|
648
|
+
block, byte, _ = _read(
|
|
649
|
+
port, stop, seq, request_buf, response_buf, stroke_buf, block, byte
|
|
650
|
+
)
|
|
637
651
|
ready_callback()
|
|
638
652
|
while True:
|
|
639
|
-
block, byte, data = _read(
|
|
653
|
+
block, byte, data = _read(
|
|
654
|
+
port, stop, seq, request_buf, response_buf, stroke_buf, block, byte
|
|
655
|
+
)
|
|
640
656
|
strokes = _parse_strokes(data)
|
|
641
657
|
for stroke in strokes:
|
|
642
658
|
callback(stroke)
|
|
@@ -650,13 +666,13 @@ class Stentura(plover.machine.base.SerialStenotypeBase):
|
|
|
650
666
|
add_callback.
|
|
651
667
|
"""
|
|
652
668
|
|
|
653
|
-
KEYS_LAYOUT =
|
|
669
|
+
KEYS_LAYOUT = """
|
|
654
670
|
# # # # # # # # # #
|
|
655
671
|
S- T- P- H- * -F -P -L -T -D
|
|
656
672
|
S- K- W- R- * -R -B -G -S -Z
|
|
657
673
|
A- O- -E -U
|
|
658
674
|
^
|
|
659
|
-
|
|
675
|
+
"""
|
|
660
676
|
|
|
661
677
|
def run(self):
|
|
662
678
|
"""Overrides base class run method. Do not call directly."""
|
plover/machine/txbolt.py
CHANGED
|
@@ -24,10 +24,12 @@ import plover.machine.base
|
|
|
24
24
|
# seen. Additionally, if there is no activity then the machine will
|
|
25
25
|
# send a zero byte every few seconds.
|
|
26
26
|
|
|
27
|
+
# fmt: off
|
|
27
28
|
STENO_KEY_CHART = ("S-", "T-", "K-", "P-", "W-", "H-", # 00
|
|
28
29
|
"R-", "A-", "O-", "*", "-E", "-U", # 01
|
|
29
30
|
"-F", "-R", "-P", "-B", "-L", "-G", # 10
|
|
30
31
|
"-T", "-S", "-D", "-Z", "#") # 11
|
|
32
|
+
# fmt: on
|
|
31
33
|
|
|
32
34
|
|
|
33
35
|
class TxBolt(plover.machine.base.SerialStenotypeBase):
|
|
@@ -39,12 +41,12 @@ class TxBolt(plover.machine.base.SerialStenotypeBase):
|
|
|
39
41
|
|
|
40
42
|
"""
|
|
41
43
|
|
|
42
|
-
KEYS_LAYOUT =
|
|
44
|
+
KEYS_LAYOUT = """
|
|
43
45
|
# # # # # # # # # #
|
|
44
46
|
S- T- P- H- * -F -P -L -T -D
|
|
45
47
|
S- K- W- R- * -R -B -G -S -Z
|
|
46
48
|
A- O- -E -U
|
|
47
|
-
|
|
49
|
+
"""
|
|
48
50
|
|
|
49
51
|
def __init__(self, params):
|
|
50
52
|
super().__init__(params)
|
|
@@ -63,7 +65,7 @@ class TxBolt(plover.machine.base.SerialStenotypeBase):
|
|
|
63
65
|
def run(self):
|
|
64
66
|
"""Overrides base class run method. Do not call directly."""
|
|
65
67
|
settings = self.serial_port.getSettingsDict()
|
|
66
|
-
settings[
|
|
68
|
+
settings["timeout"] = 0.1 # seconds
|
|
67
69
|
self.serial_port.applySettingsDict(settings)
|
|
68
70
|
self._ready()
|
|
69
71
|
while not self.finished.is_set():
|
plover/macro/repeat.py
CHANGED
plover/macro/retro.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
from plover.translation import Translation
|
|
3
2
|
from plover.steno import Stroke
|
|
4
3
|
from plover import system
|
|
@@ -13,12 +12,13 @@ def toggle_asterisk(translator, stroke, cmdline):
|
|
|
13
12
|
t = translations[-1]
|
|
14
13
|
translator.untranslate_translation(t)
|
|
15
14
|
keys = set(t.strokes[-1].steno_keys)
|
|
16
|
-
if
|
|
17
|
-
keys.remove(
|
|
15
|
+
if "*" in keys:
|
|
16
|
+
keys.remove("*")
|
|
18
17
|
else:
|
|
19
|
-
keys.add(
|
|
18
|
+
keys.add("*")
|
|
20
19
|
translator.translate_stroke(Stroke(keys))
|
|
21
20
|
|
|
21
|
+
|
|
22
22
|
def delete_space(translator, stroke, cmdline):
|
|
23
23
|
assert not cmdline
|
|
24
24
|
# Retrospective delete space
|
|
@@ -33,13 +33,14 @@ def delete_space(translator, stroke, cmdline):
|
|
|
33
33
|
if t.english is not None:
|
|
34
34
|
english.append(t.english)
|
|
35
35
|
elif len(t.rtfcre) == 1 and t.rtfcre[0].isdigit():
|
|
36
|
-
english.append(
|
|
36
|
+
english.append("{&%s}" % t.rtfcre[0])
|
|
37
37
|
if len(english) > 1:
|
|
38
|
-
t = Translation([stroke],
|
|
38
|
+
t = Translation([stroke], "{^~|^}".join(english))
|
|
39
39
|
t.replaced = replaced
|
|
40
40
|
t.is_retrospective_command = True
|
|
41
41
|
translator.translate_translation(t)
|
|
42
42
|
|
|
43
|
+
|
|
43
44
|
def insert_space(translator, stroke, cmdline):
|
|
44
45
|
assert not cmdline
|
|
45
46
|
# Retrospective insert space
|
|
@@ -50,14 +51,13 @@ def insert_space(translator, stroke, cmdline):
|
|
|
50
51
|
if replaced.is_retrospective_command:
|
|
51
52
|
return
|
|
52
53
|
lookup_stroke = replaced.strokes[-1]
|
|
53
|
-
english = [t.english or
|
|
54
|
-
for t in replaced.replaced]
|
|
54
|
+
english = [t.english or "/".join(t.rtfcre) for t in replaced.replaced]
|
|
55
55
|
if english:
|
|
56
56
|
english.append(
|
|
57
57
|
translator.lookup([lookup_stroke], system.SUFFIX_KEYS)
|
|
58
58
|
or lookup_stroke.rtfcre
|
|
59
59
|
)
|
|
60
|
-
t = Translation([stroke],
|
|
60
|
+
t = Translation([stroke], " ".join(english))
|
|
61
61
|
t.replaced = [replaced]
|
|
62
62
|
t.is_retrospective_command = True
|
|
63
63
|
translator.translate_translation(t)
|
plover/macro/undo.py
CHANGED
|
@@ -2,10 +2,11 @@ from plover.translation import Translation
|
|
|
2
2
|
from plover.oslayer.config import PLATFORM
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
if PLATFORM ==
|
|
6
|
-
BACK_STRING =
|
|
5
|
+
if PLATFORM == "mac":
|
|
6
|
+
BACK_STRING = "{#Alt_L(BackSpace)}{^}"
|
|
7
7
|
else:
|
|
8
|
-
BACK_STRING =
|
|
8
|
+
BACK_STRING = "{#Control_L(BackSpace)}{^}"
|
|
9
|
+
|
|
9
10
|
|
|
10
11
|
def undo(translator, stroke, cmdline):
|
|
11
12
|
assert not cmdline
|
plover/meta/attach.py
CHANGED
|
@@ -19,23 +19,24 @@ def meta_attach(ctx, meta):
|
|
|
19
19
|
meta = META_ATTACH_FLAG + meta + META_ATTACH_FLAG
|
|
20
20
|
begin = end = True
|
|
21
21
|
if begin:
|
|
22
|
-
meta = meta[len(META_ATTACH_FLAG):]
|
|
22
|
+
meta = meta[len(META_ATTACH_FLAG) :]
|
|
23
23
|
action.prev_attach = True
|
|
24
24
|
if end:
|
|
25
|
-
meta = meta[
|
|
25
|
+
meta = meta[: -len(META_ATTACH_FLAG)]
|
|
26
26
|
action.next_attach = True
|
|
27
27
|
action.word_is_finished = False
|
|
28
|
-
last_word = ctx.last_action.word or
|
|
28
|
+
last_word = ctx.last_action.word or ""
|
|
29
29
|
if not meta:
|
|
30
30
|
# We use an empty connection to indicate a "break" in the
|
|
31
31
|
# application of orthography rules. This allows the
|
|
32
32
|
# stenographer to tell Plover not to auto-correct a word.
|
|
33
33
|
action.orthography = False
|
|
34
34
|
elif (
|
|
35
|
-
last_word
|
|
36
|
-
not meta.isspace()
|
|
37
|
-
ctx.last_action.orthography
|
|
38
|
-
|
|
35
|
+
last_word
|
|
36
|
+
and not meta.isspace()
|
|
37
|
+
and ctx.last_action.orthography
|
|
38
|
+
and begin
|
|
39
|
+
and (not end or has_word_boundary(meta))
|
|
39
40
|
):
|
|
40
41
|
new_word = add_suffix(last_word, meta)
|
|
41
42
|
common_len = len(commonprefix([last_word, new_word]))
|
|
@@ -49,6 +50,7 @@ def meta_attach(ctx, meta):
|
|
|
49
50
|
action.word = rightmost_word(last_word + meta)
|
|
50
51
|
return action
|
|
51
52
|
|
|
53
|
+
|
|
52
54
|
def meta_carry_capitalize(ctx, meta):
|
|
53
55
|
# Meta format: ^~|content^ (attach flags are optional)
|
|
54
56
|
action = ctx.new_action()
|
|
@@ -56,12 +58,12 @@ def meta_carry_capitalize(ctx, meta):
|
|
|
56
58
|
action.next_case = Case.CAP_FIRST_WORD
|
|
57
59
|
begin = meta.startswith(META_ATTACH_FLAG)
|
|
58
60
|
if begin:
|
|
59
|
-
meta = meta[len(META_ATTACH_FLAG):]
|
|
61
|
+
meta = meta[len(META_ATTACH_FLAG) :]
|
|
60
62
|
action.prev_attach = True
|
|
61
|
-
meta = meta[len(META_CARRY_CAPITALIZATION):]
|
|
63
|
+
meta = meta[len(META_CARRY_CAPITALIZATION) :]
|
|
62
64
|
end = meta.endswith(META_ATTACH_FLAG)
|
|
63
65
|
if end:
|
|
64
|
-
meta = meta[
|
|
66
|
+
meta = meta[: -len(META_ATTACH_FLAG)]
|
|
65
67
|
action.next_attach = True
|
|
66
68
|
action.word_is_finished = False
|
|
67
69
|
if meta or begin or end:
|
plover/meta/case.py
CHANGED
|
@@ -7,6 +7,7 @@ def meta_case(ctx, case):
|
|
|
7
7
|
action.next_case = case
|
|
8
8
|
return action
|
|
9
9
|
|
|
10
|
+
|
|
10
11
|
def meta_retro_case(ctx, case):
|
|
11
12
|
case = Case(case.lower())
|
|
12
13
|
action = ctx.copy_last_action()
|
|
@@ -16,5 +17,5 @@ def meta_retro_case(ctx, case):
|
|
|
16
17
|
action.prev_replace = last_words[0]
|
|
17
18
|
action.text = apply_case(last_words[0], case)
|
|
18
19
|
else:
|
|
19
|
-
action.text =
|
|
20
|
+
action.text = ""
|
|
20
21
|
return action
|
plover/meta/conditional.py
CHANGED
|
@@ -3,13 +3,13 @@ import re
|
|
|
3
3
|
from plover.formatting import _LookAheadAction
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
IF_NEXT_META_RX = re.compile(r
|
|
7
|
-
IF_NEXT_ESCAPE_RX = re.compile(r
|
|
6
|
+
IF_NEXT_META_RX = re.compile(r"((?:[^\\/]|\\\\|\\/)*)/?")
|
|
7
|
+
IF_NEXT_ESCAPE_RX = re.compile(r"\\([\\/])")
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def meta_if_next_matches(ctx, meta):
|
|
11
11
|
pattern, result1, result2 = [
|
|
12
|
-
IF_NEXT_ESCAPE_RX.sub(r
|
|
12
|
+
IF_NEXT_ESCAPE_RX.sub(r"\1", s)
|
|
13
13
|
for s in filter(None, IF_NEXT_META_RX.split(meta, 2))
|
|
14
14
|
]
|
|
15
15
|
action_list = []
|
plover/meta/currency.py
CHANGED
|
@@ -3,16 +3,16 @@ def meta_retro_currency(ctx, dict_format):
|
|
|
3
3
|
last_words = ctx.last_words(count=1)
|
|
4
4
|
if not last_words:
|
|
5
5
|
return action
|
|
6
|
-
currency = last_words[0].replace(
|
|
6
|
+
currency = last_words[0].replace(",", "")
|
|
7
7
|
for cast, fmt in (
|
|
8
|
-
(int,
|
|
9
|
-
(float,
|
|
8
|
+
(int, "{:,}"),
|
|
9
|
+
(float, "{:,.2f}"),
|
|
10
10
|
):
|
|
11
11
|
try:
|
|
12
12
|
cast_input = cast(currency)
|
|
13
13
|
except ValueError:
|
|
14
14
|
continue
|
|
15
|
-
currency_format = dict_format.replace(
|
|
15
|
+
currency_format = dict_format.replace("c", fmt)
|
|
16
16
|
action.prev_attach = True
|
|
17
17
|
action.prev_replace = last_words[0]
|
|
18
18
|
action.text = currency_format.format(cast_input)
|
plover/meta/mode.py
CHANGED
|
@@ -15,34 +15,34 @@ def meta_mode(ctx, cmdline):
|
|
|
15
15
|
set_space:xy: Set space to xy
|
|
16
16
|
reset: Reset to normal case, space resets to ' '
|
|
17
17
|
"""
|
|
18
|
-
args = cmdline.split(
|
|
18
|
+
args = cmdline.split(":", 1)
|
|
19
19
|
mode = args.pop(0).lower()
|
|
20
20
|
action = ctx.copy_last_action()
|
|
21
|
-
if mode ==
|
|
22
|
-
action.space_char = args[0] if args else
|
|
21
|
+
if mode == "set_space":
|
|
22
|
+
action.space_char = args[0] if args else ""
|
|
23
23
|
return action
|
|
24
24
|
# No argument allowed for other mode directives.
|
|
25
25
|
if args:
|
|
26
|
-
raise ValueError(
|
|
27
|
-
if mode ==
|
|
26
|
+
raise ValueError("%r is not a valid mode" % cmdline)
|
|
27
|
+
if mode == "caps":
|
|
28
28
|
action.case = Case.UPPER
|
|
29
|
-
elif mode ==
|
|
29
|
+
elif mode == "title":
|
|
30
30
|
action.case = Case.TITLE
|
|
31
|
-
elif mode ==
|
|
31
|
+
elif mode == "lower":
|
|
32
32
|
action.case = Case.LOWER
|
|
33
|
-
elif mode ==
|
|
34
|
-
action.space_char =
|
|
35
|
-
elif mode ==
|
|
33
|
+
elif mode == "snake":
|
|
34
|
+
action.space_char = "_"
|
|
35
|
+
elif mode == "camel":
|
|
36
36
|
action.case = Case.TITLE
|
|
37
|
-
action.space_char =
|
|
37
|
+
action.space_char = ""
|
|
38
38
|
action.next_case = Case.LOWER_FIRST_CHAR
|
|
39
|
-
elif mode ==
|
|
39
|
+
elif mode == "reset":
|
|
40
40
|
action.space_char = SPACE
|
|
41
41
|
action.case = None
|
|
42
|
-
elif mode ==
|
|
42
|
+
elif mode == "reset_space":
|
|
43
43
|
action.space_char = SPACE
|
|
44
|
-
elif mode ==
|
|
44
|
+
elif mode == "reset_case":
|
|
45
45
|
action.case = None
|
|
46
46
|
else:
|
|
47
|
-
raise ValueError(
|
|
47
|
+
raise ValueError("%r is not a valid mode" % cmdline)
|
|
48
48
|
return action
|
plover/meta/punctuation.py
CHANGED
plover/misc.py
CHANGED
|
@@ -10,30 +10,32 @@ from plover.resource import ASSET_SCHEME
|
|
|
10
10
|
def popcount_8(v):
|
|
11
11
|
"""Population count for an 8 bit integer"""
|
|
12
12
|
assert 0 <= v <= 255
|
|
13
|
-
v -= (
|
|
13
|
+
v -= (v >> 1) & 0x55555555
|
|
14
14
|
v = (v & 0x33333333) + ((v >> 2) & 0x33333333)
|
|
15
15
|
return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24
|
|
16
16
|
|
|
17
|
+
|
|
17
18
|
def expand_path(path):
|
|
18
|
-
|
|
19
|
+
"""Expand path.
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
- if starting with "~/", it is substituted with the user home directory
|
|
22
|
+
- if relative, it is resolved relative to CONFIG_DIR
|
|
23
|
+
"""
|
|
23
24
|
if path.startswith(ASSET_SCHEME):
|
|
24
25
|
return path
|
|
25
26
|
path = os.path.expanduser(path)
|
|
26
27
|
path = normalize_path(os.path.join(CONFIG_DIR, path))
|
|
27
28
|
return path
|
|
28
29
|
|
|
30
|
+
|
|
29
31
|
def shorten_path(path):
|
|
30
|
-
|
|
32
|
+
"""Shorten path.
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
- if the path is below CONFIG_DIR, a relative path to it is returned
|
|
35
|
+
- if path is below the user home directory, "~/" is substituted to it
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
Note: relative path are automatically assumed to be relative to CONFIG_DIR.
|
|
38
|
+
"""
|
|
37
39
|
if path.startswith(ASSET_SCHEME):
|
|
38
40
|
return path
|
|
39
41
|
path = normalize_path(os.path.join(CONFIG_DIR, path))
|
|
@@ -41,31 +43,33 @@ def shorten_path(path):
|
|
|
41
43
|
if not config_dir.endswith(os.sep):
|
|
42
44
|
config_dir += os.sep
|
|
43
45
|
if path.startswith(config_dir):
|
|
44
|
-
return path[len(config_dir):]
|
|
45
|
-
home_dir = normalize_path(os.path.expanduser(
|
|
46
|
+
return path[len(config_dir) :]
|
|
47
|
+
home_dir = normalize_path(os.path.expanduser("~"))
|
|
46
48
|
if not home_dir.endswith(os.sep):
|
|
47
49
|
home_dir += os.sep
|
|
48
50
|
if path.startswith(home_dir):
|
|
49
|
-
return os.path.join(
|
|
51
|
+
return os.path.join("~", path[len(home_dir) :])
|
|
50
52
|
return path
|
|
51
53
|
|
|
54
|
+
|
|
52
55
|
def normalize_path(path):
|
|
53
|
-
|
|
54
|
-
'''
|
|
56
|
+
"""Normalize path: return canonical path, normalizing case on Windows."""
|
|
55
57
|
if path.startswith(ASSET_SCHEME):
|
|
56
58
|
return path
|
|
57
59
|
return os.path.normcase(os.path.realpath(path))
|
|
58
60
|
|
|
61
|
+
|
|
59
62
|
def boolean(value):
|
|
60
63
|
if isinstance(value, str):
|
|
61
64
|
v = value.lower()
|
|
62
|
-
if v in (
|
|
65
|
+
if v in ("1", "yes", "true", "on"):
|
|
63
66
|
return True
|
|
64
|
-
if v in (
|
|
67
|
+
if v in ("0", "no", "false", "off"):
|
|
65
68
|
return False
|
|
66
69
|
raise ValueError(value)
|
|
67
70
|
return bool(value)
|
|
68
71
|
|
|
72
|
+
|
|
69
73
|
def to_surrogate_pair(char):
|
|
70
74
|
pairs = []
|
|
71
75
|
for code in char:
|
plover/orthography.py
CHANGED
|
@@ -10,26 +10,27 @@ def make_candidates_from_rules(word, suffix, check=lambda x: True):
|
|
|
10
10
|
candidates = []
|
|
11
11
|
for r in system.ORTHOGRAPHY_RULES:
|
|
12
12
|
m = r[0].match(word + " ^ " + suffix)
|
|
13
|
-
if m:
|
|
13
|
+
if m:
|
|
14
14
|
expanded = m.expand(r[1])
|
|
15
15
|
if check(expanded):
|
|
16
16
|
candidates.append(expanded)
|
|
17
17
|
return candidates
|
|
18
18
|
|
|
19
|
+
|
|
19
20
|
def _add_suffix(word, suffix):
|
|
20
21
|
in_dict_f = lambda x: x in system.ORTHOGRAPHY_WORDS
|
|
21
22
|
|
|
22
23
|
candidates = []
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
alias = system.ORTHOGRAPHY_RULES_ALIASES.get(suffix, None)
|
|
25
26
|
if alias is not None:
|
|
26
27
|
candidates.extend(make_candidates_from_rules(word, alias, in_dict_f))
|
|
27
|
-
|
|
28
|
+
|
|
28
29
|
# Try a simple join if it is in the dictionary.
|
|
29
30
|
simple = word + suffix
|
|
30
31
|
if in_dict_f(simple):
|
|
31
32
|
candidates.append(simple)
|
|
32
|
-
|
|
33
|
+
|
|
33
34
|
# Try rules with dict lookup.
|
|
34
35
|
candidates.extend(make_candidates_from_rules(word, suffix, in_dict_f))
|
|
35
36
|
|
|
@@ -38,24 +39,25 @@ def _add_suffix(word, suffix):
|
|
|
38
39
|
if candidates:
|
|
39
40
|
candidates.sort(key=lambda x: system.ORTHOGRAPHY_WORDS[x])
|
|
40
41
|
return candidates[0]
|
|
41
|
-
|
|
42
|
+
|
|
42
43
|
# Try rules without dict lookup.
|
|
43
44
|
candidates = make_candidates_from_rules(word, suffix)
|
|
44
45
|
if candidates:
|
|
45
46
|
return candidates[0]
|
|
46
|
-
|
|
47
|
+
|
|
47
48
|
# If all else fails then just do a simple join.
|
|
48
49
|
return simple
|
|
49
50
|
|
|
51
|
+
|
|
50
52
|
def add_suffix(word, suffix):
|
|
51
53
|
"""Add a suffix to a word by applying the rules above
|
|
52
|
-
|
|
54
|
+
|
|
53
55
|
Arguments:
|
|
54
|
-
|
|
56
|
+
|
|
55
57
|
word -- A word
|
|
56
58
|
suffix -- The suffix to add
|
|
57
|
-
|
|
59
|
+
|
|
58
60
|
"""
|
|
59
|
-
suffix, sep, rest = suffix.partition(
|
|
61
|
+
suffix, sep, rest = suffix.partition(" ")
|
|
60
62
|
expanded = _add_suffix(word, suffix)
|
|
61
63
|
return expanded + sep + rest
|