A tool for converting AutoCorrect items into “multi match” word middles.

Post your working scripts, libraries and tools.
User avatar
kunkel321
Posts: 1172
Joined: 30 Nov 2015, 21:19

A tool for converting AutoCorrect items into “multi match” word middles.

14 Aug 2023, 15:16

Updated 8-21-2023. Below in-line code is newer than the zip.

1-11-2024 Edit: I'm removing the download because I learned that the WordWeb word list should not be reproduced.

Code: Select all

#SingleInstance
#Requires AutoHotkey v2.0
;########## WAG ######################################
; WORD ANALYZER GUI THING
; https://www.autohotkey.com/boards/viewtopic.php?f=83&t=120377
; A script to assess AutoCorrect hotstrings, while
; converting them to "word-middle" multi-match hotstrings.
; Works best with single-word AutoCorrect entries that have
; a misspelling in the middle of the word.
; USE:  Select a full hotstring (e.g. ::teh::the)
; then press Ctrl+Alt+C
; Trim off left/right letter by letter to match more words.
; When the Trigger side starts matching words, you've trimmed too much.
; Save button sends the new multi-match hotstring to the clipboard,
; for pasting the new hotstring into your AutoCorrect.ahk script.
; Ctrl+Alt+W opens blank form. ^!w again opens WordWeb app, if installed.
; Ctrl+Alt+O opens your AutoCorrect file for opening (commented-out by default).
; Requires word list (see first line of code) for HayStack.
; Author: kunkel321, Date: 8-21-2023
;###################################################

HayStack := A_ScriptDir '\WordList185k.txt' ; got rid of 1-11-2024... list is from WordWeb application.
TraySetIcon('imageres.dll', 102) ; Icon of a green badge with a checkmark.

; Startup anouncement
SoundBeep(900, 200)
SoundBeep(1200, 100)

; ---- gui setup ----
GuiColor := "F0F8FF" ; "F0F8FF" is light blue
FontColor := "003366" ; "003366" is dark blue
Wide := 180 ; Width of each side. Change if desired. Recommended: >= 160.
High := 600 ; Height of bottom edit boxes. Change if desired.
FormName := "Word Analyzer Gui Thing"

#HotIf WinActive(FormName, ) ; Only if Gui is active window.
{	Enter::GoFilter() ; filter word list
	Esc::wag.hide() ; hide gui
	^!w::LookUp() ; look up selected text in WordWeb app.
	; ^!g::LookUp() ; look up selected text in Google.
	^s::GoSave() ; save new string to clipboard
	^Shift::GoReStart() ; puts the whole trigger and replacement back.
	^z::GoUndo() ; undo last trim
	^Left::GoLChop() ; trim left char
	^Right::GoRchop() ; trim right char
	!Left:: ; set radio to 'beginning'
	{	wag['BegRadio'].Value := 1
		GoFilter()
	}
	!down:: ; set radio to 'middle'
	{	wag['MidRadio'].Value := 1
		GoFilter()
	}
	!Right:: ; set radio to 'ending'
	{	wag['EndRadio'].Value := 1
		GoFilter()
	}
	!^o::GoOpen() ; open my autocorrect file
}
#HotIf

wag := Gui('', FormName) ; "word analyzer gui"
wag.Opt("-MinimizeBox")
wag.BackColor := GuiColor
FontColor := FontColor != ""? " c" . FontColor :  ""
wag.SetFont("s11" . FontColor)

; ---- edit boxes -----
wag.SetFont('s12')
wag.addText('vTrigLabel center xm w' . wide,'Misspells')
wag.addText('vReplLabel center x+5 w' . wide,'Fixes')
wag.SetFont('s16')
wag.addEdit('vTrigNeedle xm w' . wide,)
wag.addEdit('vReplNeedle x+5 w' . wide,)

; ---- blue clue label ---
wag.SetFont('s10')
wag.addButton('vbutLtrim xm h50  w' . (wide/6), '>>').onEvent('click', GoLChop)
wag.SetFont('s14')
wag.addText('vTypoLabel center cBlue x+1 w' . (wide*5/3), FormName)
wag.SetFont('s10')
wag.addButton('vbutRtrim x+1 h50 w' . (wide/6), '<<').onEvent('click', GoRChop)

; ---- radio buttons -----
wag.SetFont('s11')
wag.addRadio('vBegRadio y+-18 x' . (wide/3),'&Beginnings').onEvent('click', GoFilter)
wag.addRadio('vMidRadio x+5 checked','&Middles').onEvent('click', GoFilter)
wag.addRadio('vEndRadio x+5','&Endings').onEvent('click', GoFilter)

; ---- bottom buttons -----
(butUndo1 := wag.addButton('xm y+3 h26 w' . (wide/3), "Undo")).OnEvent('Click', GoUndo)
(butReStart := wag.addButton('x+1 h26 w' . (wide*2/3), "ReStart")).OnEvent('Click', GoReStart)
(ButSave := wag.addButton('x+1 h26 w' . (wide*2/3), "Save")).OnEvent('Click', GoSave)
(butUndo2 := wag.addButton('x+1 h26 w' . (wide/3), "Undo")).OnEvent('Click', GoUndo)
ButUndo1.Enabled := false
ButUndo2.Enabled := false
ButReStart.Enabled := false
ButSave.Enabled := false

; ---- results lists -----
wag.SetFont('s12')
wag.addEdit('vTrigMatches xm h' . high . ' w' . wide, )
wag.addEdit('vReplMatches x+5 h' . high . ' w' . wide, )

OrigTrigger := ""
OrigReplacment := ""

twagStep := [] ; array for trigger undos
rwagStep := [] ; array for replacement undos
deltaStrs := [] ; array for previous delta strings.

; ------ functions -------
^!c::
{
		; ---- clear/reset undo history ---
	ButUndo1.Enabled := false
	ButUndo2.Enabled := false
	;~ twagStep := "" ; causes errors.
	;~ rwagStep := "" ; causes errors.
	Loop twagStep.Length
		twagStep.pop
	Loop rwagStep.Length
		rwagStep.pop
		; ---------------------------
	wag['MidRadio'].Value := 1 ; Reset Radio to "middle" each startup.
	regex := "S)(:(?:\*|\?|\w)*:)(..*)::(.*?)(?=\s;|$)\s*(;.*)?" ; swagfag-based regEx
	; OutArrays 1 = :options: | 2 = trigger | 3 = expansion when present | 4 = comment when present
	A_Clipboard:= "" ; WARNING: Previous clipboard contents are lost.
	result := ""
	Send("^c") ; copies selected text
	sleep("300")
	If RegExMatch(A_Clipboard, regex, &result) {
		wag['TrigNeedle'].Value := trim(result[2])
		sleep(200) ; prevents intermittend error on next line.
		Global OrigTrigger  := trim(result[2])
		wag['ReplNeedle'].Value := trim(result[3])
		Global OrigReplacement  := trim(result[3])
		; ---- For parse text label ----
		strT := result[2]
		strR := result[3]
		ostrT := strT ; original value (not an array)
		ostrR := strR
		LenT := strLen(strT)
		LenR := strLen(strR)

		LoopNum := min(LenT,LenR)
		strT := StrSplit(strT)
		strR := StrSplit(strR)
		Global beginning := ""
		Global typo := ""
		Global fix := ""
		Global ending := ""

		Loop LoopNum
		{ ; find matching left substring.
			bsubT := (strT[A_Index])
			bsubR := (strR[A_Index])
			If (bsubT = bsubR)
				beginning .= bsubT
			else
				break
		}

		Loop LoopNum
		{ ; Reverse Loop, find matching right substring.
			RevIndex := (LenT - A_Index) + 1
			esubT := (strT[RevIndex])
			RevIndex := (LenR - A_Index) + 1
			esubR := (strR[RevIndex])
			If (esubT = esubR)
				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

		; --- compare typo --> fix with last one. warn if the same ----
		found := false
		Global deltaStrMiddle := ""
		deltaStrMiddle := subStr(beginning, -1) . delta . subStr(ending, 1, 1)

		for index, value in deltaStrs {
			if (value = deltaStrMiddle) {
					found := true
					;nBack := A_Index ; Is this line even doing what I expect?
					nextIdx := index + 1 ; Is this line even doing what I expect?
					break
			}
		}

		if (found)
			MsgBox "------ Warning -------`n`nThe typo/misspelling part`n--------`n" deltaStrMiddle "`n--------`nappears to be the same as`nthe one that was used with`n--------`n" deltaStrs[nextIdx] "`n--------`n`n`nBe careful not to make a`nduplicate item."
			; -------------

		wag['TypoLabel'].text := deltaString ; set label at top of form.
		GoFilter()
		ButReStart.Enabled := True
		wag.Show()
	}
	Else {
		wag['TypoLabel'].text := FormName
		OpenFresh()
	}
}

^!w:: ; Just show black form.
{ OpenFresh()
}

OpenFresh()
{	wag['TrigNeedle'].Value := ""
	wag['ReplNeedle'].Value := ""
	wag['MidRadio'].Value := 1 ; Reset Radio to "middle" each startup.
	wag['TypoLabel'].text := FormName
	twagStep := [] ; array for trigger undos
	rwagStep := [] ; array for replacement undos
	GoFilter()
	wag.Show()
}

GoLChop(*) ; Trim one char from left of trigger and replacement.
{		;---- trig -----
	tText := wag['TrigNeedle'].value
	twagStep.push(tText) ; <---- save history
	tText := subStr(tText, 2)
	wag['TrigNeedle'].value := tText
		; ----- repl -----
	rText := wag['ReplNeedle'].value
	rwagStep.push(rText) ; <---- save history
	rText := subStr(rText, 2)
	wag['ReplNeedle'].value := rText
		; -----------
	ButUndo1.Enabled := true
	ButUndo2.Enabled := true
	GoFilter()
	wag.Show()
}

GoRChop(*) ; Trim one char from right of trigger and replacement.
{			; ----- trig -----
	tText := wag['TrigNeedle'].value
	twagStep.push(tText) ; <---- save history
	tText := subStr(tText, 1, strLen(tText)-1)
	wag['TrigNeedle'].value := tText
		; ----- repl -----
	rText := wag['ReplNeedle'].value
	rwagStep.push(rText) ; <---- save history
	rText := subStr(rText, 	1, strLen(rText)-1)
	wag['ReplNeedle'].value := rText
		; -----------
	ButUndo1.Enabled := true
	ButUndo2.Enabled := true
	GoFilter()
	wag.Show()
}

GoUndo(*)
{	If twagStep.Length > 0 && rwagStep.Length > 0 {
		wag['TrigNeedle'].value := twagStep.Pop()
		wag['ReplNeedle'].value := rwagStep.Pop()
		;~ If twagStep.Length = 0 && rwagStep.Length = 0
			;~ ButUndo1.Enabled := false
			;~ ButUndo2.Enabled := false ; <---- not working
		GoFilter()
		wag.Show()
	}
	else {
		ButUndo1.Enabled := false
		ButUndo2.Enabled := false
	}
}

GoReStart(*) ; ReEnters the trigger and replacement that were gotton from RegEx.
{	If !OrigTrigger and !OrigReplacment
		MsgBox("Select an autocorrect hotstring and press Ctrl+Alt+c first...")
	Else {
		result := ""
		wag['TrigNeedle'].Value := OrigTrigger
		wag['ReplNeedle'].Value := OrigReplacement
		ButUndo1.Enabled := false
		ButUndo2.Enabled := false
		twagStep := []
		rwagStep := []
		GoFilter()
		wag.Show()
	}
}

GoSave(*) ; Makes a new AutoCorrect item and saves it to clipbaord for pasting.
{	IF (wag['BegRadio'].value = 1)
		oText := ":*:"
	Else If (wag['EndRadio'].value = 1)
		oText := ":?:"
	else
		oText := ":*?:"
	tText := wag['TrigNeedle'].value
	rText := wag['ReplNeedle'].value
		; --- duplication of trigger in replacement? ---
	If (oText != ":?:") {
		tTxtLen := strLen(tText)
		rTxtFirstPart := subStr(rText, 1, tTxtLen)
		If (tText = rTxtFirstPart)
			MsgBox "------ Warning -------`n`nThe trigger`n--------`n" tText "`n--------`nis the same as the `nbeginning of the replacmeent`n--------`n" rText "`n--------`n`nIt is recommend to save this as a word `n'Ending' via radio button."
	}
		; --- any misspellings? ----
	Misspells := ""
	Misspells := wag['TrigMatches'].Value
	If (Misspells != "") {
		Misspells := StrReplace(Misspells, "`n", " () ")
		Misspells := ", but misspells " . Misspells
		;MsgBox("Misspells is:`n`n" . Misspells)
	}
	NewAC := oText . tText . "::" rText
	NewACitem := NewAC . " `; Fixes " . CurrMatches . " words" . Misspells
	A_Clipboard := NewACitem
	deltaStrs.push(deltaStrMiddle, NewAC) ; <---- save history of items.
}

GoFilter(*) ; Filter the big list of words, as needed.
{	; ======Left, Trigger side.===========
	tFind  := wag['TrigNeedle'].text
	If !tFind
		tFind := " " ; prevents error if tFind is blank.
	tFilt  := ''
	tMatches := 0
	Loop Read Haystack
	{
	  If InStr(A_LoopReadLine, tFind) {
		IF (wag['MidRadio'].value = 1) { ; Radio 'middle' is the default.
			tFilt .= A_LoopReadLine '`n'
			tMatches++
		}
		Else If (wag['EndRadio'].value = 1) {
			If InStr(SubStr(A_LoopReadLine,-StrLen(tFind)), tFind) {
				tFilt .= A_LoopReadLine '`n'
				tMatches++
			}
		}
		else { ; Not mid or end, so must be beginning.
			If InStr(SubStr(A_LoopReadLine, 1, StrLen(tFind)), tFind) {
				tFilt .= A_LoopReadLine '`n'
				tMatches++
			}
		}
	  }
	}
	 wag['TrigMatches'].Value := tFilt
	 wag['TrigLabel'].Text := "Misspells [" . tMatches . "]"

	; ====== Right, Replacement side.==========
	rFind  := wag['ReplNeedle'].text
	If !rFind
		rFind := " " ; prevents error if rFind is blank.
	rFilt  := ''
	rMatches := 0
	Loop Read Haystack
	{
	  If InStr(A_LoopReadLine, rFind) {
		IF (wag['MidRadio'].value = 1) {  ; Radio 'middle' is the default.
			rFilt .= A_LoopReadLine '`n'
			rMatches++
		}
		Else If (wag['EndRadio'].value = 1) {
			If InStr(SubStr(A_LoopReadLine,-StrLen(rFind)), rFind) {
				rFilt .= A_LoopReadLine '`n'
				rMatches++
			}
		}
		else { ; 'Beg' radio.
			If InStr(SubStr(A_LoopReadLine, 1, StrLen(rFind)), rFind) {
				rFilt .= A_LoopReadLine '`n'
				rMatches++
			}
		}
	  }
	}
	wag['ReplMatches'].Value := rFilt
	wag['ReplLabel'].Text := "Fixes [" . rMatches . "]"
		If rMatches > 1
		ButSave.Enabled := true
	Global CurrMatches := rMatches
}

; ===== Change Below Based on User Preferences ====

LookUp()
{	A_Clipboard := ""
	Send("^c") ; copies selected text
	ClipWait(1) ; Wait up to one second for clipboard.
	; If you have WordWeb app installed, use the below dll.
	; Works with free or Pro version.
	; https://wordweb.info/free/
	; If not, use the google search URL.
	Run("Rundll32 C:\Windows\WWEB32.DLL, ShowRunDLL " A_Clipboard)
	; Run("https://www.google.com/search?q=" . A_Clipboard)
}

GoOpen()
{	; A function to open autocorrect file, and go to bookmark.
	; "MY BOOKMARK" is not really a proper bookmark in SciTE.
	; The code just uses the generic Find tool to find that string in the code.
	; Assumes that SciTE is installed.
	; AHK file must be in same subfolder as this script.

	myACfile := 'MasterScript.ahk' ; PUT YOUR OWN SCRIPT NAME
	lastLoc := 'MY BOOKMARK' ; Put your own search string.
	SciTEpath := "C:\Program Files\AutoHotkey\SciTE\SciTE.exe"

	myACpath :=  A_ScriptDir "\" myACfile
	If !FileExist(myACfile)
		MsgBox "Cannot find file " myACfile ".`n`nChange 'looked-for' file at bottom of this (" A_ScriptName ")`nscript file, in the GoOpen() function."
	else
	{
		Run SciTEpath " `"" myACpath "`""
		WinWaitActive myACfile
		sleep 200
		send '^f' ; Find
		sleep 200
		send lastLoc ; types into Find dialog
		send '{Enter}'
		sleep 200
		send '{Esc}'
		sleep 200
		send '{Down 12}'
		wag.show('x40') ; Repossition WAG window a bit to the left.
	}
}

; ################ END of WAG tool ################


I’m a big fan of the AutoCorrect.ahk script compiled in 2007 by Jim Biancolo. The script provides as-you-type corrections of a few thousand common typos and misspellings. Many of the fixes are related to word-usage, such as ::arised::arose but most have simple typos such as ::arond::around
When the typo is in a common word -suffix or -prefix, or a common root-word, then the AutoCorrect entry can be converted to a multi-match word -ending, beginning, or middle, respectively; as Jim has done with these example items in the 2007 version of the script.

Code: Select all

;------------------------------------------------------------------------------
; Word endings
;------------------------------------------------------------------------------
:?:bilites::bilities
:?:bilties::bilities
:?:blities::bilities

;------------------------------------------------------------------------------
; Word beginnings
;------------------------------------------------------------------------------
:*:abondon::abandon
:*:abreviat::abbreviat
:*:accomadat::accommodat

;------------------------------------------------------------------------------
; Word middles
;------------------------------------------------------------------------------
:?*:compatab::compatib  ; Covers incompat* and compat*
:?*:catagor::categor  ; Covers subcatagories and catagories.
The above tool, the Word Analyzer Gui (WAG) Thing, helps with the process of determining which of the other (several thousand) single-use autocorrect entries can be converted into multi-fix partial-word hotstrings.

A useful multi-fix hotstring has these criteria:
-The changed-to (replacement) string matches (fixes) many English words. [Presumably it could be used with other languages too.]
-The trigger string can’t match a string present in any English word, or you’ll inadvertently misspell that word if you try to type it.
-The typo that gets corrected should be a legitimate common typo or misspelling that people make. Most of those in the 2007 script are recognized by Wikipedia as legit typos, or common misspellings.
-If the multi-fix hotstring is a word-middle, or a word end, then it can’t be used to duplicate the last letter. For example, ::fulfil::fulfill is in the 2007 list. But changing it to :*:fil::fill is not recommended because the first press of “l” will get duplicated. Thus pressing f-i-l-l will yield “filll”.

The WAG tool won’t convert the hotstrings for you, but it will let you simultaneously winnow-down the trigger string and the hotstring and compare each to a list of English words. The winnowing process usually involves trimming the prefix or suffix from a root word (assuming that the typo is in the root). It doesn’t “intelligently” remove the same letter from each string, it just removes a letter-at-a-time.
====Use====
1. Select a valid hotstring item from your AutoCorrect file.
2. Press hotkey (Ctrl+Alt+C).
3. Trim Left or Right. If the “Misspells” list has any items, you’ve trimmed too much.
4. Restart with the parsed trigger and replacement, or Undo.
5. Select a word and press Alt+Ctrl+G for Google lookup of word.
6. Press Save to save the new hotstring to the Windows Clipboard for pasting into your script.
7. Ctrl+Alt+W opens blank form.
Included in the .zip is the .ahk, a copy of AutoCorrect.exe (v2) that is renamed to match the ahk, and a word list. The word list is from the WordWeb dictionary.

====HotKeys====
Ctrl+Alt+w – open blank form
Ctrl+Alt+c – copy selected hotstring and import to tool
===Hotkeys only active if tool window is active===
Ctrl+Alt+o – open autocorrect file and browse to location (commented-out by default, define in function at bottom of code.)
Ctrl+Alt+g – copy selected word and do Google search
Ctrl+Left – trim left
Ctrl+Right – trim right
Alt+Left – set radio to “beginning”
Alt+Right – set radio to “ending”
Alt+Down – set radio to “middle”
Ctrl+z – undo trim (one at a time)
Ctrl+Shift – undo all trims (ReStart)
Enter – manual force filter

====Blue Clue Label====
Explanation of format:
beginning [ typo || correction ] ending
beginning = The matching substring at the beginning of the word (if present) that is common to hotstring and replacement. (Boolean intersection)
typo = The "typo/misspelling" part of the trigger/hotstring.
correction = The correction/replacement substring.
ending = The matching substring at the END of the word (if present) that is common to hotstring and replacement. (Boolean intersection)

Image

Notes:
-Crtl+Alt+O now opens your (actually my) AutoCorrect.ahk script and browses to where you left off. Commented-out by default, because you have to put your own script name and confirm the path for SciTE editor (see GoOpen() at bottom of code). Also... "MY BOOKMARK" is not really a proper bookmark in SciTE. The code just uses the generic Find tool to find that string in the code.
-Optimized the GoFilter() function a tiny bit.
-The Gui is kinda ugly, but I optimized it for ease of use. There are two Undo buttons, but they both do the same thing.
-Setting GuiColor and/or FontColor to “” (blank string) will result in default Windows colors.

=======

=======

Edit 8-18-2023
-Filter wasn't updating when radio buttons were changed via hotkey. Fixed.
-Added a couple warning popups.
-Improved the blue clue label.

Edit 8-21-2023
-Now things are looked up in the (installed) WordWeb app rather than Google, because it's way faster. See download link to the free version near bottom of code. The code for Google lookup is still there, just commented out.
-Changed a couple of hotkeys, so be aware of that.
-Last update introduced "a couple of warning popups." One of those was a warning for when you parse a hotstring that has the same typo/correction as the one you just saved. That has been improved with a "cumulative" history of previous items. The history resets whenever the script is killed/restarted.
-The code segments have been rearranged such that user-definable parts are either near the top, or in the two functions at the bottom (LookUp() and GoOpen()).
ste(phen|ve) kunkel

Return to “Scripts and Functions (v2)”

Who is online

Users browsing this forum: Felix Siano, hisrRB57 and 29 guests