-
Notifications
You must be signed in to change notification settings - Fork 3
/
AutoCorrect2.ahk
2059 lines (1855 loc) · 83.8 KB
/
AutoCorrect2.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#SingleInstance
SetWorkingDir(A_ScriptDir)
SetTitleMatchMode("RegEx")
#Requires AutoHotkey v2+
#Include "DateTool.ahk"
#Include "PrinterTool.ahk"
#Include "HotstringLib.ahk"
TraySetIcon(A_ScriptDir "\Icons\AhkBluePsicon.ico")
;===============================================================================
; Update date: 12-29-2024
; AutoCorrect for v2 thread on AutoHotkey forums:
; https://www.autohotkey.com/boards/viewtopic.php?f=83&t=120220
; Project location on GitHub (new versions will be on GitHub)
; https://github.com/kunkel321/AutoCorrect2
;===============================================================================
if FileExist("colorThemeSettings.ini") {
settingsFile := "colorThemeSettings.ini"
; --- Get current colors from ini file.
fontColor := IniRead(settingsFile, "ColorSettings", "fontColor")
listColor := IniRead(settingsFile, "ColorSettings", "listColor")
formColor := IniRead(settingsFile, "ColorSettings", "formColor")
}
else { ; Ini file not there, so use these color instead.
fontColor := "0x1F1F1F", listColor := "0xFFFFFF", formColor := "0xE5E4E2"
}
; Calculate contrasting text color for better readability of delta string and validation msgs.
formColor := "0x" subStr(formColor, -6) ; Make the hex value appear as a number, rather than a string.
r := (formColor >> 16) & 0xFF, g := (formColor >> 8) & 0xFF, b := formColor & 0xFF
brightness := (r * 299 + g * 587 + b * 114) / 1000
;===============================================================================
NameOfThisFile := "AutoCorrect2.ahk" ; This variable is used in the below #HotIf command for Ctrl+s: Save and Reload.
HotstringLibrary := "HotstringLib.ahk" ; Your actual library of hotstrings are added here.
; To change library name, needs to be changed (1) here, and (2) the #Include at the top.
RemovedHsFile := "RemovedHotstrings.txt" ; Also check hotstrings removed (culled) from AUTOcorrects log.
MyAhkEditorPath := "C:\Users\" A_UserName "\AppData\Local\Programs\Microsoft VS Code\Code.exe" ; <--- Only valid when VSCode is installed
; MyAhkEditorPath := "C:\Program Files\AutoHotkey\SciTE\SciTE.exe" : <--- Optionally paste another path and uncomment.
If not FileExist(MyAhkEditorPath) { ; Make sure AHK editor is assigned. Use Notepad otherwise.
MsgBox("This error means that the variable 'MyAhkEditorPath' has"
"`nnot been assigned a valid path for an editor."
"`nTherefore Notepad will be used as a substite.")
MyAhkEditorPath := "Notepad.exe"
}
If not FileExist(HotstringLibrary)
MsgBox("This message means the the hotstring library, '" HotstringLibrary "' can't be found. Please correct, and try again.")
; Initialize dictionary with path (but won't load until first use)
dict := WordNetDictionary.GetInstance(A_ScriptDir "\Dictionary\WordNet-3.0\dict\")
;===============================================================================
; Hotstring Helper 2
; Hotkey: Win + H | By: Kunkel321
; Forum thread: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=114688
; New versions posted here: https://github.com/kunkel321/AutoCorrect2
; A version of Hotstring Helper that will support block multi-line replacements and
; allow user to examine hotstring for multi-word matches. The "Examine/Analyze"
; pop-down part of the form is based on the WAG tool here
; https://www.autohotkey.com/boards/viewtopic.php?f=83&t=120377
; Customization options are below, near top of code. HotStrings will be appended (added)
; by the script at the bottom of the (now separate) Hotstring Library. Shift+Append saves
; to clipboard instead of appending. Dictionaries used: https://wordnet.princeton.edu/
; and https://gcide.gnu.org.ua/ See "class WordNetDictionary" below.
;===============================================================================
;====Change=Settings=for=Big=Validity=Dialog=Message=Box========================
myGreen := brightness > 128 ? 'c0d3803' : 'cb8f3ab' ; Color options for validity msg.
myRed := brightness > 128 ? 'cB90012' : 'cfd7c73' ; Color options for validity msg.
myBigFont := 's15' ; BigFont also used for dictionary gui and potential fixes report gui.
valOK := "-no problems found" ; Message shown when no conflicts are found.
; WARNING: The Look Up button uses VSCode shortcut keys ^f and ^g.
; Note: Depending on 'admin rights issues,' AutoCorrect2 might not be able to open
; VSCode. In such cases, open AutoCorrect2.ahk in VSCode, then use findInScript tool.
;==Miscelanceous=User=Options===================================================
hh_Hotkey := "#h" ; The activation hotkey-combo (not string) for HotString Helper, is Win+H.
;==Change=title=of=Hotstring=Helper=form=as=desired=============================
hhFormName := "HotString Helper 2" ; The name at the top of the form. Change here, if desired.
;=======Change=size=of=GUI=when="Make Bigger"=is=invoked========================
HeightSizeIncrease := 300 ; Increase by this many pixels.
WidthSizeIncrease := 400 ; Increase by this many pixels.
; The h/wFactor variables define the default (smaller) size of hh2.
hFactor := 0 ; Keep at 0.
wFactor := 366 ; 366 recommended. The buttons, etc need at least 366.
;====Assign=symbols=for="Show Symb"=button======================================
myPilcrow := "¶" ; Okay to change symbols if desired.
myDot := "• " ; adding a space (optional) allows more natural wrapping.
myTab := "⟹ " ; adding a space (optional) allows more natural wrapping.
;===Change=options=for=MULTI=word=entry=options=and=trigger=strings=as=desired==
; These are the defaults for "acronym" based boiler plate template trigger strings.
DefaultBoilerPlateOpts := "" ; PreEnter these multi-word hotstring options; "*" = end char not needed, etc.
myPrefix := ";" ; Optional character that you want suggested at the beginning of each hotstring.
addFirstLetters := 5 ; Add first letter of this many words. (5 recommended; 0 = don't use feature.)
tooSmallLen := 2 ; Only first letters from words longer than this. (Moot if addFirstLetters = 0)
mySuffix := "" ; An empty string "" means don't use feature.
;===============Change=options=AUTOCORRECT=words=as=desired=====================
; PreEnter these (single-word) autocorrect options; "T" = raw text mode, etc.
DefaultAutoCorrectOpts := "B0X" ; An empty string "" means don't use feature.
MakeFuncByDefault := 1 ; 'Make Function' box checked by default? 1 = checked.
; NOTE: If HH detects a multiline item, this gets unchecked.
;=====List=of=words=use=for=examination=lookup==================================
WordListFile := 'GitHubComboList249k.txt' ; Mostly from github: Copyright (c) 2020 Wordnik
; WordListFile := 'wlist_match6.txt' ; From https://www.keithv.com/software/wlist/
; Make sure word list is there. Change name of word list subfolder, if desired.
WordListPath := A_ScriptDir '\WordListsForHH\' WordListFile
If not FileExist(WordListPath)
MsgBox("This error means that the big list of comparison words at:`n" . WordListPath .
"`nwas not found.`n`nTherefore the 'Exam' button of the Hotstring Helper tool won't work.")
SplitPath WordListPath, &WordListName ; Extract just the name of the file.
;=====Other=Settings============================================================
; Add "Fixes X words, but misspells Y" to the end of autocorrect items?
; 1 = Yes, 0 = No. Multi-line Continuation Section items are never auto-commented.
AutoCommentFixesAndMisspells := 1
; Automatically enter the new replacement of a 'whole-word' autocorrect entry into the active edit field?
AutoEnterNewEntry := 1 ; 1 = yes, add. 0 = no, I'll manually type it.
;====Window=specific=hotkeys====================================================
; These can be edited... Cautiously.
#HotIf WinActive(hhFormName) ; Allows window-specific hotkeys.
$Enter:: ; When Enter is pressed, but only in this GUI. "$" prevents accidental Enter key loop. ; hide
{ If (SymTog.text = "Hide Symb")
return ; If 'Show symbols' is active, do nothing.
Else if ReplaceString.Focused {
Send("{Enter}") ; Just normal typing; Enter yields Enter key press.
Return
}
Else hhButtonAppend() ; Replacement box not focused, so press Append button.
}
+Left:: ; Shift+Left: Got to trigger, move cursor far left. ; hide
{ TriggerString.Focus()
Send "{Home}"
}
Esc:: ; hide
{ hh.Hide()
A_Clipboard := ClipboardOld
}
^z:: GoUndo() ; Undo last 'word exam' trims, one at a time. ; hide
^+z:: GoReStart() ; Put the whole trigger and replacement back (restart). ; hide
^Up:: ; Ctrl+Up Arrow, or ; hide
^WheelUp:: ; Ctrl+Mouse Wheel Up to increase font size (toggle, not zoom.) ; hide
{ MyDefaultOpts.SetFont('s15') ; sets at 15
TriggerString.SetFont('s15')
ReplaceString.SetFont('s15')
}
^Down:: ; Ctrl+Down Arrow, or ; hide
^WheelDown:: ; Ctrl+Mouse Wheel Down to put font size back. ; hide
{ MyDefaultOpts.SetFont('s11') ; sets back at 11
TriggerString.SetFont('s11')
ReplaceString.SetFont('s11')
}
#HotIf ; Turn off window-specific behavior.
;===== Main Graphical User Interface (GUI) is built here =======================
hh := Gui('', hhFormName)
hh.Opt("-MinimizeBox +alwaysOnTop")
try hh.BackColor := formColor ; This variable gets set at the top of the HotString Helper section.
FontColor := FontColor != "" ? "c" SubStr(fontColor, -6) : "" ; Ensure exactly one 'c' on the left.
hh.SetFont("s11 " FontColor) ; This variable gets set at the top of the HotString Helper section.
; ----- Trigger string parts ----
hh.AddText('y4 w30', 'Options')
TrigLbl := hh.AddText('x+40 w250', 'Trigger String')
listColor := listColor != "" ? "Background" . listColor : ""
MyDefaultOpts := hh.AddEdit(listColor ' yp+20 xm+2 w70 h24')
TriggerString := hh.AddEdit(listColor ' x+18 w' . wFactor -86, '')
TriggerString.OnEvent('Change', TriggerChanged)
; ----- Replacement string parts ----
hh.AddText('xm', 'Replacement')
hh.SetFont('s9')
SizeTog := hh.AddButton('x+75 yp-5 h8 +notab', 'Make Bigger')
SizeTog.OnEvent("Click", TogSize)
SymTog := hh.AddButton('x+5 h8 +notab', '+ Symbols')
SymTog.OnEvent("Click", TogSym)
hh.SetFont('s11')
ReplaceString := hh.AddEdit(listColor ' +Wrap y+1 xs h' hFactor + 100 ' w' wFactor, '')
ReplaceString.OnEvent('Change', GoFilter)
; ---- Below Replacement ----
ComLbl := hh.AddText('xm y' hFactor + 182, 'Comment')
ChkFunc := hh.AddCheckbox( 'vFunc, x+70 y' hFactor + 182, 'Make Function')
ChkFunc.OnEvent('Click', FormAsFunc)
ComStr := hh.AddEdit(listColor ' cGreen vComStr xs y' hFactor + 200 ' w' wFactor) ; Remove greed, if desired.
; ---- Buttons ----
ButApp := hh.AddButton('xm y' hFactor + 234 ' w' (wFactor/6)-4 , 'Append')
ButApp.OnEvent("Click", hhButtonAppend)
ButCheck := hh.AddButton('+notab x+5 y' hFactor + 234 ' w' (wFactor/6)-4 , 'Check')
ButCheck.OnEvent("Click", hhButtonCheck)
ButExam := hh.AddButton('+notab x+5 y' hFactor + 234 ' w' (wFactor/6)-4 , 'Exam')
ButExam.OnEvent("Click", hhButtonExam)
ButExam.OnEvent("ContextMenu", subFuncExamControl)
ButSpell := hh.AddButton('+notab x+5 y' hFactor + 234 ' w' (wFactor/6)-4 , 'Spell')
ButSpell.OnEvent("Click", hhButtonSpell)
ButLook := hh.AddButton('+notab x+5 y' hFactor + 234 ' w' (wFactor/6)-4 , 'Look')
ButLook.OnEvent("Click", hhButtonDict)
ButCancel := hh.AddButton('+notab x+5 y' hFactor + 234 ' w' (wFactor/6)-4 , 'Cancel')
ButCancel.OnEvent("Click", hhButtonCancel)
hh.OnEvent("Close", hhButtonCancel)
; ============== Bottom (toggling) "Exam Pane" part of GUI =====================
; ---- delta string ----
hh.SetFont('s10')
ButLTrim := hh.AddButton('vbutLtrim xm h50 w' (wFactor/8), '>>')
ButLTrim.onEvent('click', GoLTrim)
hh.SetFont('s14')
DeltaColor := brightness > 128 ? "191970" : "00FFFF" ; Color options for Blue Delta String.
TxtTypo := hh.AddText('vTypoLabel -wrap +center c' DeltaColor ' x+1 w' . (wFactor*3/4), hhFormName)
hh.SetFont('s10')
(ButRTrim := hh.AddButton('vbutRtrim x+1 h50 w' (wFactor/8), '<<')).onEvent('click', GoRTrim)
; ---- radio buttons -----
hh.SetFont('s11')
RadBeg := hh.AddRadio('vBegRadio y+-18 x' (wFactor/6)+7, '&Beginnings')
RadBeg.OnEvent('click', GoFilter)
RadBeg.OnEvent('contextmenu', GoRadioClick)
RadMid := hh.AddRadio('vMidRadio x+5', '&Middles')
RadMid.OnEvent('click', GoFilter)
RadMid.OnEvent('contextmenu', GoRadioClick)
RadEnd := hh.AddRadio('vEndRadio x+5', '&Endings')
RadEnd.OnEvent('click', GoFilter)
RadEnd.OnEvent('contextmenu', GoRadioClick)
; ---- bottom buttons -----
; ButUndo := hh.AddButton('xm y+3 h26 w' (wFactor+182*2), "Undo (+Reset)")
ButUndo := hh.AddButton('xm y+3 h26 w' (wFactor), "Undo (+Reset)")
ButUndo.OnEvent('Click', GoUndo)
ButUndo.Enabled := false
; ---- results lists -----
hh.SetFont('s12')
TxtTLabel := hh.AddText('vTrigLabel center y+4 h25 xm w' wFactor/2, 'Misspells')
TxtRLabel := hh.AddText('vReplLabel center h25 x+5 w' wFactor/2, 'Fixes')
EdtTMatches := hh.AddEdit(listColor ' vTrigMatches y+1 xm h' hFactor+300 ' w' wFactor/2,)
EdtRMatches := hh.AddEdit(listColor ' vReplMatches x+5 h' hFactor+300 ' w' wFactor/2,)
; ---- word list file ----
hh.SetFont('bold s8')
TxtWordList := hh.AddText('vWordList center xm y+1 h14 w' . wFactor , "Assigned word list: " WordListName)
hh.SetFont('bold s10')
; ============== Bottom (toggling) "Control Pane" part of GUI =====================
buttonConfigs := [ ; Define button configurations as array.
["Open HotString Library", Map( ; Text used for button.
"action", hhButtonOpen, ; Action when button is clicked.
"icon", "" ; Optional Icon.
)],
["Open AutoCorrection Log", Map(
"action", (*) => Run(AutoCorrectsLogFile),
"icon", ""
)],
[" Analyze AutoCorrection Log", Map( ; Space before text leaves padding for icon.
"action", (*) => Run("AcLogAnalyzer.exe"),
"icon", A_ScriptDir "\Icons\AcAnalysis.ico"
)],
["Open Backspace Context Log", Map(
"action", (*) => Run(ErrContextLog),
"icon", ""
)],
["Open Removed HotStrings List", Map(
"action", (*) => Run(RemovedHsFile),
"icon", ""
)],
["Open Manual Correction Log", Map(
"action", (*) => Run("MCLog.txt"),
"icon", ""
)],
[" Analyze Manual Correction Log", Map(
"action", (*) => Run("MCLogger.exe /script MCLogger.ahk analyze"),
"icon", A_ScriptDir "\Icons\JustLog.ico"
)],
["Report HotStrings and Potential Fixes", Map(
"action", (*) => StringAndFixReport("Button"),
"icon", ""
)]
]
if FileExist("colorThemeSettings.ini") { ; Only add this button if ini file is there.
buttonConfigs.Push([" Change Color Theme", Map(
"action", (*) => Run("ColorThemeInt.exe /script ColorThemeInt.ahk analyze"),
"icon", A_ScriptDir "\Icons\msn butterfly.ico"
)])
}
TxtCtrlLbl1 := hh.AddText("center c" DeltaColor " ym+270 h25 xm w" wFactor, "Secret Control Panel!")
hh.SetFont("s10")
buttons := Map()
for config in buttonConfigs {
buttonText := config[1]
buttonConfig := config[2]
button := hh.AddButton("y+2 h28 xm w" wFactor, buttonText)
button.OnEvent("click", buttonConfig["action"])
if buttonConfig["icon"]
try SetButtonIcon(button, buttonConfig["icon"])
buttons[buttonText] := button
}
SetButtonIcon(ButtonCtrl, IconFile) {
hIcon := DllCall("LoadImage", "Ptr", 0, "Str", IconFile, "UInt", 1, "Int", 24, "Int", 24, "UInt", 0x10)
SendMessage(0xF7, 1, hIcon, ButtonCtrl.Hwnd)
}
ShowHideButtonsControl(Visibility := false) ; Hide at startup.
ShowHideButtonsControl(Visibility := false) {
TxtCtrlLbl1.Visible := Visibility
for _, button in buttons
button.Visible := Visibility
}
ShowHideButtonExam(Visibility := False) ; Hides bottom part of GUI as default.
ShowHideButtonExam(Visibility) { ; Shows/Hides bottom, Exam Pane, part of GUI.
examCmds := [ButLTrim,TxtTypo,ButRTrim,RadBeg,RadMid,RadEnd,ButUndo
,TxtTLabel,TxtRLabel,EdtTMatches,EdtRMatches,TxtWordList]
for ctrl in examCmds {
ctrl.Visible := Visibility
}
If (Visibility = True) { ; <=======================================================<<<<
SetTimer(initDictionary, -1)
}
}
initDictionary(*) {
dict.StartBackgroundLoad()
}
ExamPaneOpen := 0, ControlPaneOpen := 0 ; Used to track pane status.
OrigTrigger := "", OrigReplacment := "" ; Used to restore original content.
origTriggerTypo := "" ; Used to determine is trigger has been changed, to potentially type new replacement at runtime.
tArrStep := [] ; array for trigger undos
rArrStep := [] ; array for replacement undos
if (A_Args.Length > 0) ; Check if a command line argument is present.
CheckClipboard() ; If present, open hh2 directly.
;===The=main=function=for=showing=the=Hotstring=Helper=Tool=====================
; This code block copies the selected text, then determines if a hotstring is present.
; If present, hotstring is parsed and HH form is populated and ExamineWords() called.
; If not, NormalStartup() function is called.
Hotkey hh_Hotkey, CheckClipboard ; Change hotkey near top, if desired.
CheckClipboard(*) {
DefaultHotStr := "" ; Clear each time.
TrigLbl.SetFont(FontColor) ; Reset color of Label, in case it's red.
EdtRMatches.CurrMatches := "" ; reset property of rep matches box.
Global ClipboardOld := ClipboardAll() ; Save and put back later.
A_Clipboard := "" ; Must start off blank for detection to work.
global A_Args
if (A_Args.Length > 0) { ; Check if a command line argument is present.
A_Clipboard := A_Args[1] ; Sent via command line, from MCLogger.
A_Args := [] ; Clear, the array after each use.
}
else { ; No cmd line, so just simulate copy like normal.
Send("^c") ; Copy selected text.
Errorlevel := !ClipWait(0.3) ; Wait for clipboard to contain text.
}
hsRegex := "(?Jim)^:(?<Opts>[^:]+)*:(?<Trig>[^:]+)::(?:f\((?<Repl>[^,)]*)[^)]*\)|(?<Repl>[^;\v]+))?(?<fCom>\h*;\h*(?:\bFIXES\h*\d+\h*WORDS?\b)?(?:\h;)?\h*(?<mCom>.*))?$" ; Jim 156
; Awesome regex by andymbody: https://www.autohotkey.com/boards/viewtopic.php?f=82&t=125100
; The regex will detect, and parse, a hotstring, whether normal, or embedded in an f() function.
thisHotStr := Trim(A_Clipboard," `t`n`r") ; For regex check, trim whitespace.
If RegExMatch(thisHotStr, hsRegex, &hotstr) {
thisHotStr := "" ; Reset to blank each use.
TriggerString.text := hotstr.Trig ; Send to top of GUI.
MyDefaultOpts.Value := hotstr.Opts
sleep(200) ; prevents intermitent error on next line.
Global OrigTrigger := hotstr.Trig
hotstr.Repl := Trim(hotstr.Repl, '"')
ReplaceString.text := hotstr.Repl
ComStr.text := hotstr.mCom ; Removes automated part of comment, leaves manual part.
Global OrigReplacement := hotstr.Repl
Global strT := hotstr.Trig
Global TrigNeedle_Orig := hotstr.Trig ; used for TriggerChnged function below.
Global strR := hotstr.Repl
; set radio buttons, based on options of copied hotstring...
If InStr(hotstr.Opts, "*") && InStr(hotstr.Opts, "?")
RadMid.Value := 1 ; Set Radio to "middle"
Else If InStr(hotstr.Opts, "*")
RadBeg.Value := 1 ; Set Radio to "beginning"
Else If InStr(hotstr.Opts, "?")
RadEnd.Value := 1 ; Set Radio to "end"
Else
RadMid.Value := 1 ; Also set Radio to "middle"
ExamineWords(strT, strR) ; We know it's not a multi-line item, so examine.
}
Else {
Global strT := A_Clipboard ; No regex match, so don't trim whitespace.
Global TrigNeedle_Orig := strT ; used for TriggerChnged function below.
Global strR := A_Clipboard
NormalStartup(strT, strR)
}
Global tMatches := 0 ; <--- Need this or can't run validiy check w/o first filtering.
; Whenever the hotkey is pressed and hh2 is launched, undo history should be reset.
; ---- clear/reset undo history ---
ButUndo.Enabled := false
Loop tArrStep.Length
tArrStep.pop
Loop rArrStep.Length
rArrStep.pop
; ---------------------------
}
; This function tries to determine if the content of the clipboard is an AutoCorrect
; item, or a selection of boilerplate text. If boilerplate text, an acronym is
; generated from the first letters. (e.g. ::ttyl::talk to you later)
isBoilerplate := 0
NormalStartup(strT, strR) { ; If multiple spaces or `n present, probably not an Autocorrect entry, so make acronym.
If ((StrLen(A_Clipboard) - StrLen(StrReplace(A_Clipboard," ")) > 2) || InStr(A_Clipboard, "`n")) {
MyDefaultOpts.text := DefaultBoilerPlateOpts
ReplaceString.value := A_Clipboard ; Use selected text for replacement string.
Global isBoilerplate := 1 ; Used below to prevent wrapping boilerplate items in f() syntax.
ChkFunc.Value := 0 ; Multi-line item, so don't make into function.
If (addFirstLetters > 0) {
initials := "" ; Initials will be the first letter of each word as a hotstring suggestion.
HotStrSug := StrReplace(A_Clipboard, "`n", " ") ; Unwrap, but only for hotstr suggestion.
Loop Parse, HotStrSug, A_Space, A_Tab {
If (Strlen(A_LoopField) > tooSmallLen) ; Check length of each word, ignore if N letters.
initials .= SubStr(A_LoopField, "1", "1")
If (StrLen(initials) = addFirstLetters) ; stop looping if hotstring is N chars long.
break
}
initials := StrLower(initials)
; Append preferred prefix or suffix, as defined above, to initials.
TriggerString.value := myPrefix initials mySuffix
}
else {
TriggerString.value := myPrefix mySuffix ; Use prefix and/or suffix as needed, but no initials.
}
}
Else If (A_Clipboard = "") {
MyDefaultOpts.Text := "", TriggerString.Text := "", ReplaceString.Text := "", ComStr.Text := "" ; Clear boxes.
RadBeg.Value := 0, RadMid.Value := 0, RadEnd.Value := 0 ; Clear radio buttons
hh.Show('Autosize yCenter')
}
else { ; No `n found so assume it's a mispelling autocorrect entry: no pre/suffix.
If (AutoEnterNewEntry = 1)
{ Global targetWindow := WinActive("A") ; Get the handle of the currently active window
Global origTriggerTypo := A_Clipboard ; Used to determine if we can type new replacement into current edit field.
}
; NOTE: Do we want the copied word to be lower-cased and trimmed of white space? Methinks, yes.
TriggerString.value := Trim(StrLower(A_Clipboard))
ReplaceString.value := Trim(StrLower(A_Clipboard))
MyDefaultOpts.text := DefaultAutoCorrectOpts
ChkFunc.Value := MakeFuncByDefault
}
; SoundBeep 800, 100
; SoundBeep 1000, 100
; SoundBeep 700, 100
ReplaceString.Opt("-Readonly")
ButApp.Enabled := true
;Global ExamPaneOpen
goFilter()
hh.Show('Autosize yCenter')
}
; The "Exam" button triggers this function. Most of this function is dedicated
; to comparing/parsing the trigger and replacement to populate the blue Delta String
; Note: We are using the term "Delta" so denote the change in the string, between trigger and replacement.
ExamineWords(strT, strR)
{ SubTogSize(hFactor, wFactor) ; Incase size is 'Bigger,' make Smaller.
hh.Show('Autosize yCenter')
ostrT := strT, ostrR := strR ; Hold original str values (not arrays).
LenT := strLen(strT), LenR := strLen(strR) ; Need length of strings.
LoopNum := min(LenT, LenR)
strT := StrSplit(strT), strR := StrSplit(strR) ; Make into arrays.
Global beginning := "", typo := "", fix := "", ending := ""
If (ostrT = ostrR) ; trig/replacement the same
deltaString := "[ " ostrT " ]"
else { ; trig/replacement not the same, so find the difference
Loop LoopNum { ; find matching left substring.
bsubT := strT[A_Index] ; bsubT "beginniing subpart of trigger"
bsubR := strR[A_Index]
If (bsubT = bsubR) ; Keep adding letters until there's a difference.
beginning .= bsubT
else
break
}
Loop LoopNum { ; Reverse Loop, find matching right substring.
RevIndex := (LenT - A_Index) + 1
esubT := strT[RevIndex] ; esubT "ending of subpart of trigger"
RevIndex := (LenR - A_Index) + 1
esubR := strR[RevIndex]
If (esubT = esubR) ; Keep adding letters until there's a difference.
ending := esubT ending
else
break
}
If (strLen(beginning) + strLen(ending)) > LoopNum { ; Overlap means repeated chars in trig or replacement.
If (LenT > LenR) { ; Trig is longer, so use T-R for str len.
delta := subStr(ending, 1, (LenT - LenR)) ; Left part of ending. Right part of beginning would also work.
delta := " [ " delta " | ] "
}
If (LenR > LenT) { ; Replacement is longer, so use R-T for str len.
delta := subStr(ending, 1, (LenR - LenT))
delta := " [ | " delta " ] "
}
}
Else {
If strLen(beginning) > strLen(ending) { ; replace shorter string last
typo := StrReplace(ostrT, beginning, "")
typo := StrReplace(typo, ending, "")
fix := StrReplace(ostrR, beginning, "")
fix := StrReplace(fix, ending, "")
}
Else {
typo := StrReplace(ostrT, ending, "")
typo := StrReplace(typo, beginning, "")
fix := StrReplace(ostrR, ending, "")
fix := StrReplace(fix, beginning, "")
}
delta := " [ " typo " | " fix " ] "
}
deltaString := beginning delta ending
} ; ------------- finished creating delta string
TxtTypo.text := deltaString ; set label at top of form.
; Call filter function then come back here.
GoFilter(True) ; Param is "via exam button"
If (ButExam.text = "Exam") {
ButExam.text := "Done"
If(hFactor != 0) {
SizeTog.text := "Make Bigger"
SoundBeep
SubTogSize(hFactor, wFactor) ; Make replacement edit box small again.
}
ShowHideButtonExam(True)
}
hh.Show('Autosize yCenter')
}
; This function toggles the size of the HH form, using the above variables.
; HeightSizeIncrease and WidthSizeIncrease determine the size when large.
; The size when small is hardcoded. Change with caution.
TogSize(*) {
If (SizeTog.text = "Make Bigger") { ; Means current state is 'Small'
SizeTog.text := "Make Smaller" ; Change the button label.
If (ButExam.text = "Done") {
ShowHideButtonExam(False)
ShowHideButtonsControl(False)
Global ExamPaneOpen := 0, ControlPaneOpen := 0
ButExam.text := "Exam"
}
Global hFactor := HeightSizeIncrease
SubTogSize(hFactor, wFactor + WidthSizeIncrease)
}
Else If (SizeTog.text = "Make Smaller") { ; Means current state is 'Big'
SizeTog.text := "Make Bigger" ; Change the button label.
Global hFactor := 0 ; hFactor is also used as param in SubTogSize(), but still need to set global var here.
SubTogSize(hFactor, wFactor)
}
hh.Show('Autosize yCenter')
}
; Called by TogSize function.
SubTogSize(hFactor, wFactor) ; Actually re-draws the form.
{ TriggerString.Move(, , wFactor -86,)
ReplaceString.Move(, , wFactor, hFactor + 100)
ComLbl.Move(, hFactor + 182, ,)
ComStr.move(, hFactor + 200, wFactor,)
ChkFunc.Move(, hFactor + 182, ,)
ButApp.Move(, hFactor + 234, ,)
ButCheck.Move(, hFactor + 234, ,)
ButExam.Move(, hFactor + 234, ,)
ButSpell.Move(, hFactor + 234, ,)
ButLook.Move(, hFactor + 234, ,)
ButCancel.Move(, hFactor + 234, ,)
}
; This function gets called from hhButtonExam (below), when Shift is down, or
; from ButExam's right-click onEvent. It shows the Control Pane.
subFuncExamControl(*) {
Global ControlPaneOpen, hFactor, HeightSizeIncrease
If (ControlPaneOpen = 1) {
ButExam.text := "Exam"
ShowHideButtonsControl(False)
ShowHideButtonExam(False)
ControlPaneOpen := 0
}
Else { ; Control Pane closed, so...
ButExam.text := "Done"
If (hFactor = HeightSizeIncrease) {
TogSize() ; Make replacement edit box small again.
SizeTog.text := "Make Bigger"
}
ShowHideButtonsControl(True)
ShowHideButtonExam(False)
ControlPaneOpen := 1
}
hh.Show('Autosize yCenter')
}
; Exam button is tripple state (Exam/Control/Closed)
; However button text is only dual state ("exam/done").
hhButtonExam(*) {
Global ExamPaneOpen, ControlPaneOpen, hFactor, HeightSizeIncrease
If ((ExamPaneOpen = 0) and (ControlPaneOpen = 0) and GetKeyState("Shift"))
|| ((ExamPaneOpen = 1) and (ControlPaneOpen = 0) and GetKeyState("Shift")) { ; Both closed, so open Control Pane.
subFuncExamControl() ; subFunction shows control pane.
}
Else If (ExamPaneOpen = 0) and (ControlPaneOpen = 0) { ; Both closed, so open Exam Pane.
ButExam.text := "Done"
If(hFactor = HeightSizeIncrease) {
TogSize() ; Make replacement edit box small again.
SizeTog.text := "Make Bigger"
}
Global OrigTrigger := TriggerString.text
Global OrigReplacement := ReplaceString.text
ExamineWords(OrigTrigger, OrigReplacement) ; Call Exam function every time Pane is opened.
goFilter()
ShowHideButtonsControl(False)
ShowHideButtonExam(True)
ExamPaneOpen := 1
}
Else { ; Close either, whatever pane is open..
ButExam.text := "Exam"
ShowHideButtonsControl(False)
ShowHideButtonExam(False)
ExamPaneOpen := 0, ControlPaneOpen := 0
}
hh.Show('Autosize yCenter')
}
; This functions toggles on/off whether the Pilcrow and other symbols are shown.
; When shown, the replacment box is set "read only" and Append is disabled.
TogSym(*) {
If (SymTog.text = "+ Symbols") {
SymTog.text := "- Symbols"
togReplaceString := ReplaceString.text
togReplaceString := StrReplace(StrReplace(togReplaceString, "`r`n", "`n"), "`n", myPilcrow . "`n") ; Pilcrow for Enter
togReplaceString := StrReplace(togReplaceString, A_Space, myDot) ; middle dot for Space
togReplaceString := StrReplace(togReplaceString, A_Tab, myTab) ; space arrow space for Tab
ReplaceString.value := togReplaceString
ReplaceString.Opt("+Readonly")
ButApp.Enabled := false
}
Else If (SymTog.text = "- Symbols") {
SymTog.text := "+ Symbols"
togReplaceString := ReplaceString.text
togReplaceString := StrReplace(togReplaceString, myPilcrow . "`r", "`r") ; Have to use `r ... weird.
togReplaceString := StrReplace(togReplaceString, myDot, A_Space)
togReplaceString := StrReplace(togReplaceString, myTab, A_Tab)
ReplaceString.value := togReplaceString
ReplaceString.Opt("-Readonly")
ButApp.Enabled := true
}
;hh.Show('Autosize yCenter')
}
; This function is called whenever the trigger (hotstring) edit box is changed.
; It assesses whether a letter has beem manually added to the beginning/ending
; of the trigger, and adds the same letter to the replacement edit box.
; BUGGY... Doesn't always do anything. :(
TriggerChanged(*)
{ Global ExamPaneOpen, TrigNeedle_Orig
TrigNeedle_New := TriggerString.text
If (TrigNeedle_New != TrigNeedle_Orig) && (ExamPaneOpen = 1) { ; If trigger has changed and pane open.
If (TrigNeedle_Orig = SubStr(TrigNeedle_New, 2, )) { ; one char added on the left left box
tArrStep.push(TriggerString.text) ; Save history for Undo feature.
rArrStep.push(ReplaceString.text) ; Save history.
ReplaceString.Value := SubStr(TrigNeedle_New, 1, 1) ReplaceString.text ; add same char to left of other box
}
If (TrigNeedle_Orig = SubStr(TrigNeedle_New, 1, StrLen(TrigNeedle_New)-1)) { ; one char added on the right or left box
tArrStep.push(TriggerString.text) ; Save history for Undo feature.
rArrStep.push(ReplaceString.text) ; Save history.
ReplaceString.text := ReplaceString.text SubStr(TrigNeedle_New, -1, ) ; add same char on other side.
}
TrigNeedle_Orig := TrigNeedle_New ; Update the "original" string so it can detect the next change.
}
ButUndo.Enabled := true
goFilter()
}
; This function detects that the "[] Make Function" checkbox was ticked.
; It puts/removes the needed hotstring options, then beeps.
FormAsFunc(*) {
If (ChkFunc.Value = 1) {
MyDefaultOpts.text := "B0X" StrReplace(StrReplace(MyDefaultOpts.text, "B0", ""), "X", "")
SoundBeep 700, 200
}
else {
MyDefaultOpts.text := StrReplace(StrReplace(MyDefaultOpts.text, "B0", ""), "X", "")
SoundBeep 900, 200
}
}
; Runs a validity check. If validiy problems are found, user is given option to append anyway.
hhButtonAppend(*) {
Global tMyDefaultOpts := MyDefaultOpts.text
Global tTriggerString := TriggerString.text
Global tReplaceString := ReplaceString.text
ValidationFunction(tMyDefaultOpts, tTriggerString, tReplaceString)
If Not InStr(CombinedValidMsg, valOK, , , 3) ; Msg doesn't have three occurrences of valOK.
biggerMsgBox(CombinedValidMsg, 1)
else { ; no validation problems found
Appendit(tMyDefaultOpts, tTriggerString, tReplaceString)
}
}
; Calls the validity check, but doesn't append the hotstring.
hhButtonCheck(*) {
If winExist('Validity Report') ; Toggles showing the big box.
bb.Destroy()
else {
Global tMyDefaultOpts := MyDefaultOpts.text
Global tTriggerString := TriggerString.text
Global tReplaceString := ReplaceString.text
ValidationFunction(tMyDefaultOpts, tTriggerString, tReplaceString)
biggerMsgBox(CombinedValidMsg, 0)
}
}
; An easy-to-see large dialog to show Validity report/warning.
; Select text from the trigger report box and press Look Up button.
; Selected text is sent to find box in VSCode. If digits, sent to go-to-line.
currentEdit := 0, bb := 0
biggerMsgBox(thisMess, secondButt) {
global bb, fontColor, formColor, myBigFont, mbTitle := "", currentEdit
if (IsObject(bb))
bb.Destroy()
bb := Gui(,'Validity Report')
bb.BackColor := formColor
bb.SetFont('s11 ' fontColor)
mbTitle := bb.Add('Text',, 'For proposed new item:')
mbTitle.Focus()
bb.SetFont(myBigFont)
proposedHS := ':' tMyDefaultOpts ':' tTriggerString '::' tReplaceString
bb.Add('Text', (strLen(proposedHS)>90? 'w600 ':'') 'xs yp+22', proposedHS)
bb.SetFont('s11')
secondButt=0? bb.Add('Text',, "===Validation Check Results==="):''
bb.SetFont(myBigFont)
bbItem := StrSplit(thisMess, "*|*")
If InStr(bbItem[2],"`n",,,10)
bbItem2 := subStr(bbItem[2], 1, inStr(bbItem[2], "`n",,,10)) "`n## Too many conflicts to show in form ##"
Else
bbItem2 := bbItem[2]
edtSharedSettings := ' -VScroll ReadOnly -E0x200 Background'
bb.Add('Edit', (inStr(bbItem[1], valOK)? myGreen : myRed) edtSharedSettings formColor, bbItem[1])
trigEdtBox := bb.Add('Edit', (strLen(bbItem2)>104? ' w600 ' : ' ') (inStr(bbItem2, valOK)? myGreen : myRed) edtSharedSettings formColor, bbItem2)
bb.Add('Edit', (strLen(bbItem[3])>104? ' w600 ' : ' ') (inStr(bbItem[3], valOK)? myGreen : myRed) edtSharedSettings formColor, bbItem[3])
bb.SetFont('s11 ' FontColor)
secondButt=1? bb.Add('Text',,"==============================`nAppend HotString Anyway?"):''
bbAppend := bb.Add('Button', , 'Append Anyway')
bbAppend.OnEvent('Click', (*) => (Appendit(tMyDefaultOpts, tTriggerString, tReplaceString), bb.Destroy()))
if secondButt != 1
bbAppend.Visible := False
bbClose := bb.Add('Button', 'x+5 Default', 'Close')
bbClose.OnEvent('Click', (*) => bb.Destroy())
if (bbItem[4] = 1) {
bbLookup := bb.Add('Button', 'x+12', 'Look Up')
bbLookup.OnEvent('Click', (*) => processSelectedText(lookupSelectedText))
trigEdtBox.OnEvent('Focus', (*) => (currentEdit := trigEdtBox))
}
bb.Show('yCenter x' (A_ScreenWidth/2))
WinSetAlwaysontop(1, "A")
bb.OnEvent('Escape', (*) => bb.Destroy())
}
processSelectedValText(callback) { ; Claude AI wrote this function.
if !IsObject(currentEdit) {
return
}
EM_GETSEL := 0xB0
start := 0
end := 0
DllCall("SendMessage", "Ptr", currentEdit.hwnd, "Uint", EM_GETSEL, "Ptr*", &start, "Ptr*", &end)
if (start != end) {
text := StrReplace(currentEdit.Value, "`n", "`r`n")
selected := SubStr(text, start + 1, end - start)
A_Clipboard := selected
word := Trim(A_Clipboard)
if (word != "") {
callback(word)
}
}
}
lookupSelectedText(text) {
if not WinExist(HotstringLibrary) {
Run MyAhkEditorPath " " HotstringLibrary
While not WinExist(HotstringLibrary)
Sleep 50
}
WinActivate HotstringLibrary
If RegExMatch(text, "^\d{2,}")
SendInput "^g" text
else {
SendInput "^f"
Sleep 200
SendInput "^v"
}
mbTitle.Focus()
}
; This function runs several validity checks.
ValidationFunction(tMyDefaultOpts, tTriggerString, tReplaceString) {
GoFilter() ; This ensures that "rMatches" has been populated. <--- had it commented out for a while, then put back.
Global CombinedValidMsg := "", validHotDupes := "", validHotMisspells := "", ACitemsStartAt
HsLibContents := Fileread(HotstringLibrary) ; Save these contents to variable 'HsLibContents'.
If (tMyDefaultOpts = "") ; If options box is empty, skip regxex check.
validOpts := valOK
else { ;===== Make sure hotstring options are valid ========
NeedleRegEx := "(\*|B0|\?|SI|C|K[0-9]{1,3}|SE|X|SP|O|R|T)" ; These are in the AHK docs I swear!!!
WithNeedlesRemoved := RegExReplace(tMyDefaultOpts, NeedleRegEx, "") ; Remove all valid options from var.
If (WithNeedlesRemoved = "") ; If they were all removed...
validOpts := valOK
else { ; Some characters from the Options box were not recognized.
OptTips := inStr(WithNeedlesRemoved, ":")? "Don't include the colons.`n":""
OptTips .= " ; a block text assignment to var
(
Common options. From AHK docs
* - ending char not needed
? - trigger inside other words
C - case-sensitive
--don't use below with f function--
B0 - no backspacing (leave trigger)
SI - send input mode
SE - send event mode
Kn - set key delay (n is a digit)
O - omit end char
R - raw dog it
)"
validOpts .= "Invalid Hotsring Options found.`n---> " WithNeedlesRemoved "`n" OptTips
}
}
;==== Make sure hotstring box content is valid ========
validHot := "", showLookupBox := 0 ; Reset each time.
If (tTriggerString = "") || (tTriggerString = myPrefix) || (tTriggerString = mySuffix)
validHot := "HotString box should not be empty."
Else If InStr(tTriggerString, ":")
validHot := "Don't include colons."
else { ; No colons, and not empty. Good. Now check for duplicates.
Loop Parse, HsLibContents, "`n", "`r" { ; Check line-by-line.
If (A_Index < ACitemsStartAt) or (SubStr(trim(A_LoopField, " `t"), 1,1) != ":")
continue ; Will skip non-hotstring lines, so the regex isn't used as much.
If RegExMatch(A_LoopField, "i):(?P<Opts>[^:]+)*:(?P<Trig>[^:]+)", &loo) { ; loo is "current loopfield"
If (tTriggerString = loo.Trig) and (tMyDefaultOpts = loo.Opts) { ; full duplicate triggers
validHotDupes := "`nDuplicate trigger string found at line " A_Index ".`n---> " A_LoopField
showLookupBox := 1
Continue
} ; No duplicates. Look for conflicts...
If (InStr(loo.Trig, tTriggerString) and inStr(tMyDefaultOpts, "*") and inStr(tMyDefaultOpts, "?"))
|| (InStr(tTriggerString, loo.Trig) and inStr(loo.Opts, "*") and inStr(loo.Opts, "?")) { ; Word-Middle Matches
validHotDupes .= "`nWord-Middle conflict found at line " A_Index ", where one of the strings will be nullified by the other.`n---> " A_LoopField
showLookupBox := 1
Continue
}
If ((loo.Trig = tTriggerString) and inStr(loo.Opts, "*") and not inStr(loo.Opts, "?") and inStr(tMyDefaultOpts, "?") and not inStr(tMyDefaultOpts, "*"))
|| ((loo.Trig = tTriggerString) and inStr(loo.Opts, "?") and not inStr(loo.Opts, "*") and inStr(tMyDefaultOpts, "*") and not inStr(tMyDefaultOpts, "?")) { ; Rule out: Same word, but beginning and end opts
validHotDupes .= "`nDuplicate trigger found at line " A_Index ", but maybe okay, because one is word-beginning and other is word-ending.`n---> " A_LoopField
showLookupBox := 1
Continue
}
If (inStr(loo.Opts, "*") and loo.Trig = subStr(tTriggerString, 1, strLen(loo.Trig)))
|| (inStr(tMyDefaultOpts, "*") and tTriggerString = subStr(loo.Trig, 1, strLen(tTriggerString))) { ; Word-Beginning Matches
validHotDupes .= "`nWord Beginning conflict found at line " A_Index ", where one of the strings is a subset of the other. Whichever appears last will never be expanded.`n---> " A_LoopField
showLookupBox := 1
Continue
}
If (inStr(loo.Opts, "?") and loo.Trig = subStr(tTriggerString, -strLen(loo.Trig)))
|| (inStr(tMyDefaultOpts, "?") and tTriggerString = subStr(loo.Trig, -strLen(tTriggerString))) { ; Word-Ending Matches
validHotDupes .= "`nWord Ending conflict found at line " A_Index ", where one of the strings is a superset of the other. The longer of the strings should appear before the other, in your code.`n---> " A_LoopField
showLookupBox := 1
Continue
}
}
Else ; not a regex match, so go to next item in loop.
continue
}
; Added later... Check if proposed item is duplicate of a previousl removed item...
If FileExist(RemovedHsFile) { ; If there is a "Removed Strings" file, save the contents for a variable.
loop parse FileRead(RemovedHsFile), "`n"
If RegExMatch(A_LoopField, "i):(?P<Opts>[^:]+)*:(?P<Trig>[^:]+)", &loo) ; loo is "current loopfield"
If (tTriggerString = loo.Trig) and (tMyDefaultOpts = loo.Opts) {
validHotDupes .= "`nWarning: A duplicate trigger string was previously removed.`n----> " A_LoopField
Continue
}
}
If validHotDupes != ""
validHotDupes := SubStr(validHotDupes, 2) ; Trim `n from beginning.
If (tMatches > 0){ ; This error message is collected separately from the loop, so both can potentially be reported.
validHotMisspells := "This trigger string will misspell [" tMatches "] words."
}
if validHotDupes and validHotMisspells
validHot := validHotDupes "`n-" validHotMisspells ; neither is blank, so new line
else If !validHotDupes and !validHotMisspells ; both are blank, so no validity concerns.
validHot := valOK
else
validHot := validHotDupes validHotMisspells ; one (and only one) is blank so concantinate
}
;==== Make sure replacement string box content is valid ===========
If (tReplaceString = "")
validRep := "Replacement string box should not be empty."
else if (SubStr(tReplaceString, 1, 1) == ":") ; If Replacement box empty, or first char is ":"
validRep := "Don't include the colons."
else if (tReplaceString = tTriggerString)
validRep := "Replacement string SAME AS Trigger string."
else
validRep := valOK
; Concatenate the three above validity checks.
CombinedValidMsg := "OPTIONS BOX `n" validOpts "*|*HOTSTRING BOX `n" validHot "*|*REPLACEMENT BOX `n" validRep "*|*" showLookupBox
Return CombinedValidMsg ; return result for use in Append or Validation functions.
} ; end of validation func
; The "Append It" function actually combines the hotsring components and
; appends them to the script, then reloads it.
Appendit(tMyDefaultOpts, tTriggerString, tReplaceString) {
WholeStr := "", tComStr := '', aComStr := '' ; tComStr is "text of comment string." aComStr is "auto comment string."
tMyDefaultOpts := MyDefaultOpts.text
tTriggerString := TriggerString.text
tReplaceString := ReplaceString.text
If (rMatches > 0) and (AutoCommentFixesAndMisspells = 1) { ; AutoCom var set near top of code.
Misspells := EdtTMatches.Value
If (tMatches > 3) ; More than 3 misspellings?
Misspells := ", but misspells " tMatches . " words !!! "
Else If (Misspells != "") { ; Any misspellings? List them, if <= 3.
Misspells := SubStr(StrReplace(Misspells, "`n", " (), "), 1, -2) . ". "
Misspells := ", but misspells " Misspells
}
aComStr := "Fixes " rMatches " words " Misspells
aComStr := StrReplace(aComStr, "Fixes 1 words ", "Fixes 1 word ")
}
; Add function part if needed. Combine the parts into a single- or multi-line hotstring.
If (chkFunc.Value = 1) and (isBoilerplate = 0) and not InStr(tReplaceString, "`n") { ; Function.
tMyDefaultOpts := "B0X" StrReplace(StrReplace(tMyDefaultOpts, "B0", ""), "X", "")
If (ComStr.text != "") || (aComStr != "")
tComStr := " `; " aComStr ComStr.text
WholeStr := ":" tMyDefaultOpts ":" tTriggerString "::f(`"" tReplaceString "`")" tComStr
}
Else If InStr(tReplaceString, "`n") { ; Combine the parts into a muli-line hotstring.
tMyDefaultOpts := StrReplace(StrReplace(tMyDefaultOpts, "B0", ""), "X", "")
openParenth := subStr(tReplaceString, -1) = "`t"? "(RTrim0`n" : "(`n" ; If last char is Tab, use LTrim0.
WholeStr := ":" tMyDefaultOpts ":" tTriggerString "::" tComStr "`n" openParenth tReplaceString "`n)"
}
Else { ; Plain vanilla, single-line, no function.
tMyDefaultOpts := StrReplace(tMyDefaultOpts, "X", "")
WholeStr := ":" tMyDefaultOpts ":" tTriggerString "::" tReplaceString tComStr
}
If GetKeyState("Shift") { ; User held Shift when clicking Append Button.
A_Clipboard := WholeStr
ToolTip("Copied to clipboard.")
SetTimer () => ToolTip(), -2000
}
else {
FileAppend("`n" WholeStr, HotstringLibrary) ; 'n makes sure it goes on a new line.
If (AutoEnterNewEntry = 1) ; If this user setting (at the top) is set, then call function
ChangeActiveEditField()
If not getKeyState("Ctrl")
Reload() ; relaod the script so the new hotstring will be ready for use; but not if ctrl pressed.
}
} ; Newly added hotstrings will be way at the bottom of the ahk library file.
; This function only gets called if the new AC item is a single-word fix.
; It assesses whether the text in the target document is still selected, and if trigger is unchanged,
; and if so, corrects the item in the target document.
ChangeActiveEditField(*) {
A_Clipboard := ""
Send("^c") ; Copy selected text.
Errorlevel := !ClipWait(0.3) ; Wait for clipboard to contain text.
Global origTriggerTypo := trim(origTriggerTypo) ; Remove any whitespace.