A helpful editing tool.

Post your working scripts, libraries and tools for AHK v1.1 and older
l6908lay
Posts: 43
Joined: 09 Apr 2023, 05:16

A helpful editing tool.

Post by l6908lay » 27 Nov 2023, 03:58

Hello I've been working on a Editor tool that can read a "dictionary or file of words to help in writing. This actually does help quite well but it is a bit buggy in certain scenarios. Some know bugs with it are when you tap Tab the Words window doesn't shut off... There may be limitations to the size of file that will work readily and speedy with the application... Too many words, with the same first beginning, may come up with redundant information (work around for redundancy may be to place a string split variable counter to help determine what you may most likely use instead of the first closest match).

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
#SingleInstance force
DetectHiddenWindows, On ; used to find the hidden window Words

Global Cache:=% A_WorkingDir "\Cache"
if !FileExist(Cache)
	FileCreateDir, % Cache ; Used as a convenient location for the "dictionary" 


Gui, AI:New, +AlwaysOnTop +Resize +toolWindow 
Gui, Add, Edit, r2 ym x100 vMyEdit1 w200, as
Gui, Margin, 50, 20
Gui, Show, , AI work
Gui, Words:New, +AlwaysOnTop -SysMenu -Caption
Gui, Add, Listbox, r6 gWord vWord x0 y0,
Gui, margin, 0, 0
Gui, Show, , Words
WinHide, Words ; Hiding this window so it doesn't interfere too much with the edit window above.
#Persistent
SetTimer, Tick, on ; Timer to search for the change in window "AI"
return

Tick()
{
	CoordMode, Caret, Screen ; Collecting the caret location of the "AI" window relative to the screen so the listbox can be drawn beside it.
	WinActivate, AI ; Activating Window AI just in case it isn't (Not that it shouldn't be if you are typing in it but, for other edit locations it's helpful)
	Winset, AlwaysOnTop, Off, AI ; placing the "AI" window below the Listbox
	Y:=% A_CaretY+11 ; gaining y caret at insert location in "AI" window + 11 so it appears slightly below the edit field
	WinMove, Words, , %A_CaretX%, %Y% ; Moving window "Words" to the caret position.
	GuiControlGet, text, AI:, Edit1 ; Collecting the information in "AI" Window
	Loop, Parse, % text, % A_Space ; Parsing the information by spaces to collect the last word being worked on.
	{
		val:=% A_LoopField ; val will carry the last iteration of the loop being parsed in text variable.
	}
	text:=% val ; setting text to equal val (mostly because I prefer using text over val)
	str:= StrLen(text) ; collecting string length to determine later if the listbox needs to appear or not
	IniRead, val, Recorder.ini, Focus, Key2 ; reusing val variable.
	if (str != val) ; this is to prevent the persistent loop from redrawing, over and over again, Words window
	{
		IniWrite, % str, Recorder.ini, Focus, Key2 ; to keep up with when string length changes
		if (str > 3) ; This is a personal decision if the word is longer than 3 it activates of course this can be any value you choose.
		{
			descriptions:=DesGet() ; collects the information from a file and corrects any original errors that might exist
			words:=MyFixed(descriptions) ; changes the information to remove any other issues that might exist (i.e. special characters any non-letter characters except "|" pipelines)
			Sort, words, U D| ; sort is used in case the original had multiple same words
			Loop, Parse, % words, | ; parse words by "|" pipeline
			{
				var1:= SubStr(A_LoopField, 1, str) ; collecting "like" variables with the same number of characters as the "Edit1" from "AI" window to resolve for matches
				if (var1 == var2) ; checking to make sure both values are the same or not
				{
					z++ ; the first occurrence will come with a double "||" pipeline to make it first selected
					if (z == 1) ; invoking the first occurrence with the double "||" pipeline
						List:=% List A_LoopField "||" ; the variable "List is not necessary in the full variable expression I just do it for completion
					else if (z != 1) ; the rest will come with a single "|" pipeline
						List:=% List A_LoopField "|"
				}
			}
			if (List != "") ; when searching for values it's quite possible that nothing is found "either a new word or a made up word"
			{
				WinShow, Words ; activating window "words"
				Winset, Top ; bringing it to the top just in case AI Always on top makes a mistake
				GuiControl, Words:, Listbox1, | ; clearing the listbox in case there were already results.
				GuiControl, Words:, Listbox1, % List ; Creating the listbox with the information collected
			}
		}
		if (str < 4) ; if string is less then 4 hides the window if it isn't already
			WinHide, Words
	}
}

Tab:: ; hotkey to activate Word g-sub
Word: ; Word g-sub
GuiControlGet, text, AI:, Edit1 ; Collect the text from window "AI"
GuiControlGet, Word, Words:, Listbox1 ; Collect the selected (double piped) word from the listbox in window "Word"
WinHide, Words ; Hides words so it's not in the way (seems buggy to actually do it)
Loop, Parse, % text, % A_Space ; parsing the words in "AI" Edit box for the last word created if you create large enough data with {ctr-lf} returns {enter} `n +{enter} so on you may want to add the delimiter for that first.
{
	val:=% A_LoopField ; last word found
}
NS:= RegExReplace(text, val "$", Word) ; Replace the last occurrence of the last word in the "AI" window edit box with the new "word" value from the listbox selected value from "Word" window.
GuiControl, AI:Text, Edit1, % NS ; Change the value of the "AI" window Edit text value
WinActivate, AI ; Activate AI (just in case it didn't)
Send, {End}{End} ; when you change the value of the "AI" window Edit Box it places the insert at the beginning instead of the end... My work around is quite buggy actually maybe Clipboard:= % NS and ControlSend..  or Send, ^c^v
Winset, AlwaysOnTop, On, AI ; resetting "AI" window back on top.
return

MyFixed(string) ; This is personal changes of my own data edit or disregard where you want...
{
	StringReplace, string, string, (*Inner thought[lmn]) (, , All
	StringReplace, string, string, {*What I'm doing[lmn]} {, , All
	StringReplace, string, string, Chris Says:  , , All
	StringReplace, string, string, (*Chris thinks[lmn]) ( , , All
	StringReplace, string, string, {*Chris is doing[lmn]} { , , All
	StringReplace, string, string, (*Luke thinks[lmn])  (, , All
	StringReplace, string, string, {*Luke is doing[lmn]} {, , All
	StringReplace, string, string, *[10], , All
	StringReplace, string, string, "[10], , All
	StringReplace, string, string, }[10], , All
	StringReplace, string, string, )[10], , All
	StringReplace, string, string, `n`r, , All
	StringReplace, string, string, `n, , All
	StringReplace, string, string, `r, , All
	var:= SubStr(string, 1, 1)
	if (var == "*")
	{
		str:=StrLen(string)
		string:=SubStr(string, 2, str)
	}
	nv = "
	if (var == nv)
	{
		str:=StrLen(string)
		string:= SubStr(string, 2, str)
	}
	StringReplace, string, string, ., % A_Space, All
	StringReplace, string, string, |, % A_Space, All
	StringReplace, string, string, !, % A_Space, All
	StringReplace, string, string, ?, % A_Space, All
	StringReplace, string, string, ", % A_Space, All
	StringReplace, string, string, *, % A_Space, All
	StringReplace, string, string, `,, % A_Space, All
	StringReplace, string, string, % A_Space A_Space, % A_Space, All
	StringReplace, string, string, % A_Space, |, All
	StringReplace, string, string, `n, |, All
	return % string
}

DesGet() ; this is to collect the information from the location designed for the "descriptions" or words or dictionary.
{
	FileRead, descriptions, % Cache "\descriptions.txt" ; don't forget to rename your file whatever you choose.
	if Not ErrorLevel
	{
		Loop, Parse, % descriptions, `n
		{
			if (A_LoopField == "")
				Continue
			IfNotInstring, list, % A_LoopField ; trying to delimit repeating information before it goes to next result.
				list:=% list A_LoopField "|" ; it might seem to be redundant to place this then string replace it later but to be completely honest this particular code is part of a much larger program in the works and there it is needed to be placed.
		}
		descriptions:=% list
	}
	Return % descriptions
}

AIGuiClose: ; gui control to close the application on window "AI"
esc:: ; Hotkey to shut down the app
exitapp

User avatar
andymbody
Posts: 940
Joined: 02 Jul 2017, 23:47

Re: A helpful editing tool.

Post by andymbody » 27 Nov 2023, 06:51

I've not seen this before. Can you clarify the purpose of using :=% and % with return?
Cache:=% A_WorkingDir "\Cache"
Return % descriptions

Why not?
Cache := A_WorkingDir "\Cache"
Return descriptions

l6908lay
Posts: 43
Joined: 09 Apr 2023, 05:16

Re: A helpful editing tool.

Post by l6908lay » 27 Nov 2023, 11:30

andymbody wrote:
27 Nov 2023, 06:51
I've not seen this before. Can you clarify the purpose of using :=% and % with return?
Cache:=% A_WorkingDir "\Cache"
Return % descriptions

Why not?
Cache := A_WorkingDir "\Cache"
Return descriptions
It's just a preference. I means specifically either way works but I prefer this way.

l6908lay
Posts: 43
Joined: 09 Apr 2023, 05:16

Updated Editor tool

Post by l6908lay » 28 Nov 2023, 16:48

In my previous Editor tool there was a few bugs I did notice I have updated that Editor tool to reflect a better rendition of my imagined work. This still has some already known bugs. For instance backspace to edit a previous word doesn't invoke the words window, it doesn't fix inner text (text that has text around it), and currently no way to "send" to other windows or put in clipboard for pasting. Either way I believe it's a good start. I hope you enjoy it.

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory. ; this can be modified to any location
#SingleInstance force ; this prevents the script from popping up the dialogue box warning if you start the program without shutting it down.
DetectHiddenWindows, On ; unnecessary but left just in case of future changes.

Global Cache:=% A_WorkingDir "\Cache"
if !FileExist(Cache)
	FileCreateDir, % Cache ; a working Cache designed for the dictionary, descriptions, words.


Gui, AI:New, +AlwaysOnTop +Resize +toolWindow ; naming the AI window and it's tools.. This may be later changed to suite setting and so on.
Gui, Add, Edit, r2 ym x100 vWords w200 hwndTracker, ; added a window handle "tracker" for future work. I believe it might be possible to work inside the text instead of the end of line..
Gui, Margin, 50, 20 ; just a border provided to the edit box
Gui, Show, , AI work ; renaming the window and showing it.
Gui, Words:New, +AlwaysOnTop -SysMenu -Caption ; adding a new window I might add a window handle later.
Gui, Add, Listbox, r6 gWord vWord x0 y0, ; a sub-label and subroutine for the listbox.
Gui, margin, 0, 0 ; the only thing visible will be the listbox.
Gui, Show, , Words ; allowing the Words window to be seen.
WinSet, Transparent, 0, Words ; instead of winhide I decided to go with transparent. I found that winshow activates the window which affects the flow of keyboard input.
WinActivate, AI ; initiating AI window so it's active instead of having to activate it.
#Persistent
SetTimer, Tick, on ; Timer set for word checking in editor versus what's in the dictionary. It is a permanent loop recalling itself with no stop.
return

Tick()
{
	IfWinNotActive, AI
		return
	IfWinActive, AI
	{
		CoordMode, Caret, Screen ; getting Caret by screen
		CoordMode, Mouse, Screen ; getting mouse by screen (I know it's not used but I am thinking maybe somewhere I might use it... IDK)
		GuiControlGet, InFocus, AI:Focus ; This collects the infocus location of window AI Box (i.e. edit1 edit2 etc...)
		GuiControlGet, text, AI:, % InFocus ; Collecting text of AI window in Edit1
		Loop, Parse, % text, % A_Space ; parsing the loop by spaces to get the last worked on word.
		{
			val:=% A_LoopField ; I may later try to change this in the event of "inner" word work. Such as a reedit of sentences.
		}
		text:=% val ; renaming text to equal val Mostly to reduce memory.. plus I like text it's more readable.
		str:= StrLen(text) ; collecting string length
		var2:= SubStr(text, 1, str) ; a little redundant but copy paste var1 var2 is faster... 
		if (InFocus == "Edit1") ; in the event I plan to add extra edit boxes.
		{
			IniRead, val, Recorder.ini, Edit1, Key1 ; val of Edit1 Key1
			IniRead, strc, Recorder.ini, Edit1, Key2 ; strc of Edit1 Key2 
			IniRead, window, Recorder.ini, Edit1, Key3 ; window value of Edit1 key3
			if (val == "ERROR") ; if Recorder.ini doesn't exist or doesn't have values values are set.
			{
				IniFixer(InFocus, str, "Done", "Start") ; Auto-written into the Ini fixer function
				val:=% "Done"
				strc:=% str
				window:=% "Start" ; setting the values.
			}
		}
		if (val == "Done") ; val can only equal "Start" or "Done" I may implement other types. The val variable declares done when the word has been fixed. This is so it doesn't remain an active window after you press tab. 
			WinSet, Transparent, 0, Words ; Transparency seems to work better than winshow. winshow affects the activity of AI and causes issues when typing text (skipping letters)
		if (str < 2) ; This is primarily used to reset "Done" value of val variable and window variable. It is assumed if the val of the string is less than 2 it must be a new word beginning and a full rewrite of values needs placed.
			IniFixer(InFocus, str, "Start", "Start") ; resetting the Ini values to reflect the new word changes.
		if (val != "Done") ; This is to prevent the script from attempting to go any further. Mostly if the val is "Done" then you have finished writing a word and a new word hasn't begun.
		{
			if (str != strc) ; this prevents the script from going further unless a string length change has occurred from the previous loop iteration of Tick timer.
			{
				IniFixer(InFocus, str, "Start", window) ; fixes the string length change. the "Start" could actually be val.
				if (str > 3) ; if the str is greater than three (meaning string length is greater than 3 characters) it goes on. It's a personal preference edit as you choose.
				{
					if (window != "Done") ; Prevents a flickering action I noticed when working with the previous.
					{
						IniWrite, Done, Recorder.ini, % InFocus, Key3 ; Directly writing "Done" when this is the first time through.
						Y:=% A_CaretY+11 ; setting the "Y" caret location of the window to the caret and just below it. 
						WinMove, Words, , %A_CaretX%, %Y% ; The previous one followed the typing action this one stays at where the insert was.  It caused a flickering action of the "words" window.
						Winset, AlwaysOnTop, Off, AI ; deactivates always on top so that the transparent window words automatically comes to the top.
						WinSet, Transparent, Off, Words ; turns off the transparency of Words
						WinActivate, AI ; technically this isn't necessary but in case it isn't active (for whatever reason) it will be now. (by this point you have almost done ~50-150ms or more)
					}
					descriptions:=DesGet() ; gaining the descriptions from the file of words.
					words:=MyFixed(descriptions) ; editing those descriptions to be in the correct format by stringreplace.
					Sort, words, U D| ; Sorting the words for duplicates by the pipeline.
					Loop, Parse, % words, | ; parsing the words by the pipeline.
					{
						var1:= SubStr(A_LoopField, 1, str) ; Collecting the value of var1 substring A_LoopField by the string length of the text from AI window.
						if (var1 == var2) ; Looks for exact matches of var1 and var2 from A_LoopField and text.
						{
							z++ ; a addition of z which resets because it isn't declared defined or set (in other words a local variable).
							IfNotInString, List, % A_LoopField ; preventing like words from being in the list. I have found occasionally words still get through and further filtering may need done.
							{
								if (z == 1) ; On the first match a double pipeline is implemented to make it first to use on Tab or g-sub of Words.
									List:=% List A_LoopField "||" ; List isn't necessary technically before the A_LoopField but I do it mostly for completion (plus copy and paste ease).
								else if (z != 1) ; any other words that match in results that wasn't the first only get 1 pipeline.
									List:=% List A_LoopField "|" ; I placed List first in the expression so it comes in a particular order.
							}
						}
					}
					if (List != "") ; from time to time the list might be empty (new word that doesn't exist in the file of words or misspelling of a word)
					{
						GuiControl, Words:, Listbox1, | ; resets the listbox to nothing
						GuiControl, Words:, Listbox1, % List ; places the List in the Listbox
					}
					if (List == "") ; if the List is empty.
					{
						IniFixer(InFocus, str, "Done", window) ; Fixes the Ini data and sets the var to done so it doesn't repeat.
						WinSet, Transparent, 0, Words ; Makes the window words transparent.
					}
				}
				else if (str < 4)
					WinSet, Transparent, 0, Words ; if the stringlength is less than 4 it makes the window transparent.
			}
		}
	}
	List:= "" ; Clears the Variable. Sometimes if the variable isn't cleared residual values will remain in the List causing confusion (because it is more like a static variable than a local).
	return
}

$Tab:: ; placing a $ operator helps to allow the `t character to be sent (via send) without activating the Hotkey.
Word: ; g-sublabel from the window words.
WinGet, visible, Transparent, Words ; a variable to "see" if the window is transparent or not.
if (visible != 0) ; if the window is visible (not equal to 0)
{
	WinActivate, AI ; activates AI but might not need to do that actually as it probably already is active.
	GuiControlGet, InFocus, AI:Focus ; Gains the name of the AI window Edit box.
	GuiControlGet, text, AI:, % InFocus ; gets the text of the AI edit box text.
	GuiControlGet, Word, Words:, Listbox1 ; gets the selected "||" word of the listbox from words.
	Loop, Parse, % text, % A_Space ; parses the text of AI window to get the last word being worked on.
	{
		val:=% A_LoopField ; val will carry the last word being worked on.
	}
	NS:= RegExReplace(text, val "$", Word) ; RegEx replace the val from the text in the last occurrence of the text to the new Word from the Listbox.
	Winset, AlwaysOnTop, On, AI ; resets AI to always on top.
	Clipboard:=% NS ; places the changes in clipboard
	SetKeyDelay , 250 ; sets a key delay to prevent a mistaken send and no text sent.
	ControlSend, % InFocus, ^a^v, AI ; sends the Control a (selects all) control v (pastes) to put the clipboard contents in the Edit box.
	IniFixer(InFocus, "0", "Done", "Start") ; Fixes the ini data to reflect done and resets the string value to 0.
	WinSet, Transparent, 0, Words ; Sets words back to transparent.
	Clipboard:="" ; Clears Clipboard for next use (and releases memory).
	return
}
IfWinActive, AI ; In the event the window is invisible and you want to sent tab to the edit box this will send it to the last spot in the text box.
{
	GuiControlGet, InFocus, AI:Focus
	GuiControlGet, text, AI:, % InFocus
	Clipboard:=% text "`t"
	ControlSend, % InFocus, ^a^v, AI
}
else ; in the event you are working anywhere else it will send tab anywhere else with no issues. (the great use of $ without setting off the Hotkey).
	Send, {Tab}
return

IniFixer(InFocus, str, val, window) ; there are many edit boxes only the first is used here I am currently processing 5 edit boxes in my personal work.
{
	if (InFocus == "Edit1")
	{
		IniWrite, % val, Recorder.ini, % InFocus, Key1
		IniWrite, % str, Recorder.ini, % InFocus, Key2
		IniWrite, % window, Recorder.ini, % InFocus, Key3
	}
	if (InFocus == "Edit2")
	{
		IniWrite, % val, Recorder.ini, % InFocus, Key1
		IniWrite, % str, Recorder.ini, % InFocus, Key2
		IniWrite, % window, Recorder.ini, % InFocus, Key3
	}
	if (InFocus == "Edit3")
	{
		IniWrite, % val, Recorder.ini, % InFocus, Key1
		IniWrite, % str, Recorder.ini, % InFocus, Key2
		IniWrite, % window, Recorder.ini, % InFocus, Key3
	}
	if (InFocus == "Edit4")
	{
		IniWrite, % val, Recorder.ini, % InFocus, Key1
		IniWrite, % str, Recorder.ini, % InFocus, Key2
		IniWrite, % window, Recorder.ini, % InFocus, Key3
	}
	if (InFocus == "Edit5")
	{
		IniWrite, % val, Recorder.ini, % InFocus, Key1
		IniWrite, % str, Recorder.ini, % InFocus, Key2
		IniWrite, % window, Recorder.ini, % InFocus, Key3
	}
	return
}

MyFixed(string) ; a series of string replace commands to eliminate special characters and so on. 
{
	StringReplace, string, string, (*Inner thought[lmn]) (, , All
	StringReplace, string, string, {*What I'm doing[lmn]} {, , All
	StringReplace, string, string, Chris Says:  , , All
	StringReplace, string, string, (*Chris thinks[lmn]) ( , , All
	StringReplace, string, string, {*Chris is doing[lmn]} { , , All
	StringReplace, string, string, (*Luke thinks[lmn])  (, , All
	StringReplace, string, string, {*Luke is doing[lmn]} {, , All
	StringReplace, string, string, *[10], , All
	StringReplace, string, string, "[10], , All
	StringReplace, string, string, }[10], , All
	StringReplace, string, string, )[10], , All
	StringReplace, string, string, `n`r, , All
	StringReplace, string, string, `n, , All
	StringReplace, string, string, `r, , All
	var:= SubStr(string, 1, 1)
	if (var == "*")
	{
		str:=StrLen(string)
		string:=SubStr(string, 2, str)
	}
	nv = "
	if (var == nv)
	{
		str:=StrLen(string)
		string:= SubStr(string, 2, str)
	}
	StringReplace, string, string, ., % A_Space, All
	StringReplace, string, string, |, % A_Space, All
	StringReplace, string, string, !, % A_Space, All
	StringReplace, string, string, ?, % A_Space, All
	StringReplace, string, string, ", % A_Space, All
	StringReplace, string, string, *, % A_Space, All
	StringReplace, string, string, `,, % A_Space, All
	StringReplace, string, string, % A_Space A_Space, % A_Space, All
	StringReplace, string, string, % A_Space, |, All
	StringReplace, string, string, `n, |, All
	return % string
}

DesGet() ; gets the original text from the descriptions text file.
{
	FileRead, descriptions, % Cache "\descriptions.txt"
	if Not ErrorLevel
	{
		Loop, Parse, % descriptions, `n
		{
			if (A_LoopField == "")
				Continue
			IfNotInstring, list, % A_LoopField
				list:=% list A_LoopField "|"
		}
		descriptions:=% list
	}
	Return % descriptions
}

AIGuiClose:
esc::
exitapp

[Moderator note: Merged this post from a different thread.]

l6908lay
Posts: 43
Joined: 09 Apr 2023, 05:16

I'll call this version v0.0.9 of Helper tool.

Post by l6908lay » 29 Nov 2023, 23:50

Current known limitations is in "Space bar save." The time it takes to actually send the "Space" the script can be done with it before the action of pressing space. Sleep times are included but I'm not 100% sure it is 100% reliable (though on my 50 uses of it it worked quite well actually). If you notice after pressing space in the INI file "MasterControl" "Key1"it is still "Working" you may need to adjust sleep time (some people like using message boxes for data and work flow. I have found that INI write gets a lot of it without messing up the flow of the program). Other Limitations Include placing text after a word (i.e. "a possible solution|") where you attempt to continue after solution without a space. I also believe I have stressed the limitations of #persistent loops and will simply need to move to a main loop function. The difference in Persistent and a main loop will be a better use of variables... So IniWrite (though a good tool for workflow and data checking) is not as good as process data manipulation using variables that change in process (Like static variables). I'm not sure if a workaround can be made for the aforementioned problem (typing directly after text with no space) but I will try.

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
#SingleInstance force ; this prevents the script from popping up the dialogue box warning if you start the program without shutting it down.
DetectHiddenWindows, On ; unnecessary but left just in case of future changes.

Global Cache:=% A_WorkingDir "\Cache"
if !FileExist(Cache)
	FileCreateDir, % Cache ; a working Cache designed for the dictionary, descriptions, words.

CoordMode, Caret, Screen
CoordMode, Mouse, Screen

Gui, AI:New, +AlwaysOnTop +Resize +toolWindow ; naming the AI window and it's tools.. This may be later changed to suite setting and so on.
Gui, Add, Edit, r2 ym x100 vWords w200 hwndTracker, this is a long string of words to use. It is to help in getting a large amount of words to test in the possibility of edit and editing controls. ; added a window handle "tracker" for future work. I believe it might be possible to work inside the text instead of the end of line..
Gui, Margin, 50, 20 ; just a border provided to the edit box
Gui, Show, , AI work ; renaming the window and showing it.
Gui, Words:New, +AlwaysOnTop -SysMenu -Caption +toolWindow ; adding a new window I might add a window handle later.
Gui, Add, Listbox, r6 gWord vWord x0 y0, ; a sub-label and subroutine for the listbox.
Gui, margin, 0, 0; the only thing visible will be the listbox.
Gui, Show, , Words ; allowing the Words window to be seen.
WinSet, Transparent, 0, Words ; instead of winhide I decided to go with transparent. I found that winshow activates the window which affects the flow of keyboard input.
WinActivate, AI ; initiating AI window so it's active instead of having to activate it.
GuiControlGet, InFocus, AI:Focus ;<------- This text is primarily for start quality 
GuiControlGet, text, AI:, % InFocus ;<------- This text is primarily for start quality 
IniFixer(InFocus, "0", "Start", "Start") ;<------- This text is primarily for start quality 
IniWrite, Start, Recorder.ini, MasterControl, Key1 ;<------- This text is primarily for start quality 
FileMove, % Cache "\Temp.txt", % Cache "\Tem.txt", 1 ;<------- This text is primarily for start quality 
FileAppend, % text, % Cache "\temp.txt", UTF-8
#Persistent
SetTimer, Tick, on ; Timer set for word checking in editor versus what's in the dictionary. It is a permanent loop recalling itself with no stop.
return

Tick()
{
	IfWinNotActive, AI
		return
	IfWinActive, AI
	{
		GuiControlGet, InFocus, AI:Focus ; This collects the infocus location of window AI Box (i.e. edit1 edit2 etc...)
		GuiControlGet, text, AI:, % InFocus
		IniRead, n, Recorder.ini, MasterControl, Key1 ; Collecting text of AI window in Edit1
		if (n == "ERROR") ; In the first time reading of an ini value if it is blank it comes back with "ERROR" as it's code
		{
			IniWrite, Start, Recorder.ini, MasterControl, Key1 ; Setting keys to have a value
			FileAppend, % text, % Cache "\temp.txt", UTF-8
		}
		FileRead, contents, % Cache "\Temp.txt" ; A temp cache location was created to store and read for changes.
		IniRead, n, Recorder.ini, MasterControl, Key1 ; Using a variable to initiate find differences.
		if (contents == text)
		{
			if ((n == "Working")||(n == "Done")) ; reset any values not reset already.
			{
				IniFixer(InFocus, "0", "Start", "Start")
				IniWrite, Start, Recorder.ini, MasterControl, Key1
				IniWrite, 0, Recorder.ini, MasterControl, Key2 ; y is stored for later use.
				IniWrite, "", Recorder.ini, MasterControl, Key3 ; no text to collect as its at the end of the line.
			}
		}
		else if (contents != text) ; using the contents of the cache helps to eliminate the need to process to far in the loop.
		{
			if (n == "Start")
			{
				IniFixer(InFocus, "0", "Start", "Start")
				IniWrite, Working, Recorder.ini, MasterControl, Key1 ; Ini changed value from start to working.
				wordarray:=[] ; Creating an array to check values against.
				Loop, Parse, % contents, % A_Space ; parsing contents of "Temp" file
				{
					x++
					wordarray["ValuePos" x] :=  A_LoopField ; Creating an associative array for exact location and word.
				}
				x:=0 ; resetting x for later use.
				Loop, Parse, % text, % A_Space ; parsing the "Edit" box to check against the array values.
				{
					y++
					nstring:= wordarray["ValuePos" y] ; y is the delimiter for the line. By using the y value I count 1 at a time and get the value.
					if (nstring == "") ; if nstring from the array is blank a new word at the end of the line is being created.
					{
						IniWrite, % y, Recorder.ini, MasterControl, Key2 ; y is stored for later use.
						IniWrite, "", Recorder.ini, MasterControl, Key3 ; no text to collect as its at the end of the line.
						Break ; ends the loop so no other values are retained.
					}
					if (nstring != A_LoopField) ; this is where inside work is being done by the user on words in the "Edit" box
					{
						IfInString, A_LoopField, %nstring% ; This is an additional word being added to the already going text.
						{
							IniWrite, % y, Recorder.ini, MasterControl, Key2 ; recording both the y and nstring value for later use.
							IniWrite, % nstring, Recorder.ini, MasterControl, Key3
							Break
						}
						IfNotInString, A_LoopField, %nstring% ; this would be a deleted or modified word the user has changed or removed.
						{
							IniWrite, % y, Recorder.ini, MasterControl, Key2 ; storing the y variable and the LoopField (word that's been modified or deleted)
							IniWrite, % A_LoopField, Recorder.ini, MasterControl, Key3
							Break
						}
					}
				}
			}
			IniRead, v, Recorder.ini, MasterControl, Key2 ; Collecting the information from the above recieved information
			IniRead, nstring, Recorder.ini, MasterControl, Key3
			Loop, Parse, % text, % A_Space ; parsing the "Edit" box text 
			{
				x++
				if (x == v) ; if x == v (or the y value where it was previously found) the LoopField is saved in a var.
					text:=% A_LoopField
			}
			text:= RegExReplace(text, nstring "$", "") ; The orginal text being worked on is necessary. So RegEx the nstring from the obtained original reslut (which might be "", or a word)
			str:= StrLen(text) ; I might use this later to create a  aesthetic value of "selecting (or highlighting) the text as it's being updated.
			var2:= SubStr(text, 1, str) ; redundante from text. (basically text == var2). But var is quick to copy and paste.
			if (InFocus == "Edit1") ; in the event of more "Edit" boxes added one day.
			{
				IniRead, val, Recorder.ini, Edit1, Key1 ; gaining the values to check against for only doing what's necessary (helps to speed up the process by not doing redundant processes).
				IniRead, strc, Recorder.ini, Edit1, Key2
				IniRead, window, Recorder.ini, Edit1, Key3
				if (val == "ERROR") ; In the first time reading of an ini value if it is blank it comes back with "ERROR" as it's code
				{
					IniFixer(InFocus, str, "Done", "Start")
					val:=% "Done"
					strc:=% str
					window:=% "Start"
				}
			}
			if (val == "Done") ; The value of done is first and formost to set window "words" to transparent.
				WinSet, Transparent, 0, Words
			if (str < 2) ; this is second to look for a less then statement to set the IniFixer values for restart.
				IniFixer(InFocus, str, "Start", "Start")
			if (val != "Done") ; if The value isn't equal to done then it passes this information. (1 minor issue with this is "Done" may already be existent When ini wrote "Start" But, that really shouldn't be a problem since I delimit by 4 or greater)
			{
				if (str != strc) ; if a change in string length occurs
				{
					WinGet, visible, Transparent, Words ; a variable to "see" if the window is transparent or not.
					if (visible != 0) ; if the window is visible (not equal to 0)
					{
						IniRead, n, Recorder.ini, MasterControl, Key1
						if (n == "Working")
							IniWrite, Start, Recorder.ini, MasterControl, Key1
					}
					IniFixer(InFocus, str, "Start", window) ; IniWrite values.
					if (str > 3) ; if string length is greater than 3
					{
						if (window != "Done") ; if window isn't done yet
						{
							IniWrite, Done, Recorder.ini, % InFocus, Key3 ; IniWrite Done.
							window:=% "Done" ; Set Window to done just in case.
							Y:=% A_CaretY+11 ; Set caret value below insert.
							WinMove, Words, , %A_CaretX%, %Y% ; move Window "words" to caret location.
							Winset, AlwaysOnTop, Off, AI ; turn off always on top on Window "AI"
							WinSet, Transparent, Off, Words ; Turn of transparency of Window "words" I have found winshow affects typing input.
							WinActivate, AI ; activate Window "AI" just in case.
						}
						descriptions:=DesGet() ; get list of words, dictionary, or whatever (dictionary is not included so you will have to either created it or download)
						words:=MyFixed(descriptions) ; removing "Special characters, and adding pipelines"
						Sort, words, U D| ; sorting by "|" pipelines for duplicates.
						Loop, Parse, % words, | ; parsing by pipelines
						{
							var1:= SubStr(A_LoopField, 1, str) ; creating a substring that is the same length as var2
							if (var1 == var2) ; checking for absolute equality.
							{
								z++
								IfNotInString, List, % A_LoopField ; Sometimes "it seems" duplicates still will exist maybe a loop parse error (IDK but I add this to ensure only 1 result {Might be residual spaces that are existent after MyFixed method})
								{
									if (z == 1) ; The first occurance gets a double pipeline.
										List:=% List A_LoopField "||"
									else if (z != 1) ; all other occurances get a single pipeline
										List:=% List A_LoopField "|"
								}
							}
						}
						if (List != "") ; Sometimes the list can be empty.
						{
							GuiControl, Words:, Listbox1, | ; Clears the list So new list can be updated.
							GuiControl, Words:, Listbox1, % List ; sets the new list
						}
						if (List == "") ; Sometimes the list can be empty. No word found or the word is just mispelled.
						{
							IniFixer(InFocus, str, "Done", window) ; Changing the settings of IniFixer
							WinSet, Transparent, 0, Words ; Makes the Window "words" transparent.
						}
					}
					else if (str < 4) ; if the string is less than 4 the window is transparent.
						WinSet, Transparent, 0, Words
				}
			}
		}
	}
	List:= "" ; Clear list so residual data is removed.
	return
}

~Backspace:: ; Backspace is now a hotkey the "~" tilde is to allow Backspace to act normally while doing something.
IfWinActive, AI ; Checks if window "AI" is active and changes IniFixer as needed. (The user is modifying a word.)
{
	GuiControlGet, InFocus, AI:Focus
	GuiControlGet, text, AI:, % InFocus
	IniFixer(InFocus, "0", "Start", "Start")
	IniWrite, Start, Recorder.ini, MasterControl, Key1
}
return

~Space:: ; On Space the Change is updated to the "Temp" file to reflect user changes.
IfWinActive, AI ; Checks if window "AI" is active and changes IniFixer as needed. (The user is modifying a word.)
{
	sleep, 150 ; <-- the sent Space is actually slower than the process below. Which places the "MasterControl" "Key1" in "Working" I placed it to 150 because 50 ms was still too slow sometimes.
	GuiControlGet, InFocus, AI:Focus
	GuiControlGet, text, AI:, % InFocus
	IniFixer(InFocus, "0", "Start", "Start")
	IniWrite, Start, Recorder.ini, MasterControl, Key1
	FileMove, % Cache "\Temp.txt", % Cache "\Tem.txt", 1
	FileAppend, % text, % Cache "\temp.txt", UTF-8
	WinSet, Transparent, 0, Words ; puting words in transparent.
}
return

$Tab:: ; placing a $ operator helps to allow the `t character to be sent (via send) without activating the Hotkey.
Word: ; g-sub-label is added 
WinGet, visible, Transparent, Words ; a variable to "see" if the window is transparent or not.
if (visible != 0) ; if the window is visible (not equal to 0)
{
	IniRead, v, Recorder.ini, MasterControl, Key2 ; getting y variable from the Tick loop 
	IniRead, nstring, Recorder.ini, MasterControl, Key3 ; getting the nstring variable from the Tick loop.
	WinActivate, AI ; activates AI but might not need to do that actually as it probably already is active.
	GuiControlGet, InFocus, AI:Focus ; Gains the name of the AI window Edit box.
	GuiControlGet, text, AI:, % InFocus ; gets the text of the AI edit box text.
	MsgBox, % v "`n" nstring
	GuiControlGet, Word, Words:, Listbox1 ; gets the selected "||" word of the listbox from words.
	y:=0 ; reset y for next use
	Loop, Parse, % text, % A_Space ; parses the text of AI window to get the last word being worked on.
	{
		y++
		if (y == v) ; if y == v the loop location is equal to the place user is editing.
		{
			MsgBox, % A_LoopField "`n" v "`n" y
			wordvar:=% A_LoopField  ; setting wordvar to equal the LoopField
		}
	}
	NS:= RegExReplace(wordvar, wordvar, Word) ; replace wordvar with Word
	IniWrite, % NS ", " wordvar, Recorder.ini, checker, key1
	save:=% Clipboard ; saving Clipboard information (incase user has the Clipboard with something in it) I also know I could use ClipSaved.
	Clipboard:= ""
	str:= StrLen(wordvar) ; Getting string length of wordvar
	str2:= StrLen(nstring) ; Getting string length of nstring
	str:=(str-str2) ; subracting str from str2 
	if (str == 0) ; from time to time it is understandble the user pre-places the space to correct. in the event of that str == 0
		str:=% str2 ; from there we'll just set str to str2 to avoid a non-deleted edit that was supposed to be replaced.
	IniWrite, % str ", " str2 ", " nstring, Recorder.ini, checker, key3
	Winset, AlwaysOnTop, On, AI ; setting window "AI" on top.
	Clipboard:=% NS ; Puting NS RegEx into Clipboard
	IniWrite, % Clipboard, Recorder.ini, checker, key2
	ControlSend, % InFocus,  +{Left %str%}^v, AI ; selecting only the characters the user is working on and pasting over them.
	WinSet, Transparent, 0, Words ; puting words in transparent.
	GuiControlGet, text, AI:, % InFocus ; getting the updated information.
	FileMove, % Cache "\Temp.txt", % Cache "\Tem.txt", 1 ; moving Temp file to new Tem file. (I prefer this over delete).
	FileAppend, % text, % Cache "\Temp.txt", UTF-8 ; Appending text to new file Temp.
	Clipboard:=% save ; putting back the Clipboard to it's orginal state.
	list:= ; the variable list is empty.
	save:= "" ; the variable save is empty.
	IniWrite, Start, Recorder.ini, MasterControl, Key1 ; updating the changes to Ini.
	IniWrite, 0, Recorder.ini, MasterControl, Key2 ; y is stored for later use.
	IniWrite, "", Recorder.ini, MasterControl, Key3 ; no text to collect as its at the end of the line.
	IniFixer(InFocus, "0", "Done", "Start") ; Fixing IniFixer
	wordvar:= "" ; the variable wordvar is now empty
	return
}
IfWinActive, AI ; In the event the window is invisible and you want to sent tab to the edit box this will send it to the last spot in the text box.
{
	GuiControlGet, InFocus, AI:Focus
	GuiControlGet, text, AI:, % InFocus
	Clipboard:=% text "`t"
	ControlSend, % InFocus, ^a^v, AI
}
else ; in the event you are working anywhere else it will send tab anywhere else with no issues. (the great use of $ without setting off the Hotkey).
	Send, {Tab}
return

IniFixer(InFocus, str, val, window) ; there are many edit boxes only the first is used here I am currently processing 5 edit boxes in my personal work.
{
	if (InFocus == "Edit1")
	{
		IniWrite, % val, Recorder.ini, % InFocus, Key1
		IniWrite, % str, Recorder.ini, % InFocus, Key2
		IniWrite, % window, Recorder.ini, % InFocus, Key3
	}
	if (InFocus == "Edit2")
	{
		IniWrite, % val, Recorder.ini, % InFocus, Key1
		IniWrite, % str, Recorder.ini, % InFocus, Key2
		IniWrite, % window, Recorder.ini, % InFocus, Key3
	}
	if (InFocus == "Edit3")
	{
		IniWrite, % val, Recorder.ini, % InFocus, Key1
		IniWrite, % str, Recorder.ini, % InFocus, Key2
		IniWrite, % window, Recorder.ini, % InFocus, Key3
	}
	if (InFocus == "Edit4")
	{
		IniWrite, % val, Recorder.ini, % InFocus, Key1
		IniWrite, % str, Recorder.ini, % InFocus, Key2
		IniWrite, % window, Recorder.ini, % InFocus, Key3
	}
	if (InFocus == "Edit5")
	{
		IniWrite, % val, Recorder.ini, % InFocus, Key1
		IniWrite, % str, Recorder.ini, % InFocus, Key2
		IniWrite, % window, Recorder.ini, % InFocus, Key3
	}
	return
}

MyFixed(string) ; a series of string replace commands to eliminate special characters and so on. 
{
	StringReplace, string, string, (*Inner thought[lmn]) (, , All
	StringReplace, string, string, {*What I'm doing[lmn]} {, , All
	StringReplace, string, string, Chris Says:  , , All
	StringReplace, string, string, (*Chris thinks[lmn]) ( , , All
	StringReplace, string, string, {*Chris is doing[lmn]} { , , All
	StringReplace, string, string, (*Luke thinks[lmn])  (, , All
	StringReplace, string, string, {*Luke is doing[lmn]} {, , All
	StringReplace, string, string, *[10], , All
	StringReplace, string, string, "[10], , All ; " <--- this is being done because of my edited language encoder for AHK.
	StringReplace, string, string, }[10], , All
	StringReplace, string, string, )[10], , All
	StringReplace, string, string, `n`r, , All
	StringReplace, string, string, `n, , All
	StringReplace, string, string, `r, , All
	var:= SubStr(string, 1, 1) ; for beginning of string 
	if (var == "*")
	{
		str:=StrLen(string)
		string:=SubStr(string, 2, str)
	}
	nv = " ; " <--- this is being done because of my edited language encoder for AHK.
	if (var == nv)
	{
		str:=StrLen(string)
		string:= SubStr(string, 2, str)
	}
	StringReplace, string, string, ., % A_Space, All
	StringReplace, string, string, |, % A_Space, All
	StringReplace, string, string, !, % A_Space, All
	StringReplace, string, string, ?, % A_Space, All
	StringReplace, string, string, ", % A_Space, All ; " <--- this is being done because of my edited language encoder for AHK.
	StringReplace, string, string, *, % A_Space, All
	StringReplace, string, string, `,, % A_Space, All
	StringReplace, string, string, % A_Space A_Space, % A_Space, All
	StringReplace, string, string, % A_Space, |, All
	StringReplace, string, string, `n, |, All
	return % string
}

DesGet() ; gets the original text from the descriptions text file.
{
	FileRead, descriptions, % Cache "\descriptions.txt"
	if Not ErrorLevel
	{
		Loop, Parse, % descriptions, `n
		{
			if (A_LoopField == "")
				Continue
			IfNotInstring, list, % A_LoopField
				list:=% list A_LoopField "|"
		}
		descriptions:=% list
	}
	Return % descriptions
}

AIGuiClose:
esc::
IniWrite, Start, Recorder.ini, MasterControl, Key1
IniWrite, 0, Recorder.ini, MasterControl, Key2
IniWrite, "", Recorder.ini, MasterControl, Key3
exitapp

[Moderator note: Several posts from here forward were merged from a different thread.]

l6908lay
Posts: 43
Joined: 09 Apr 2023, 05:16

Re: I'll call this version v0.0.9 of Helper tool.

Post by l6908lay » 30 Nov 2023, 20:04

This is the blank slate version without any hotkeys using the main loop feature. I am still considering asthetic values of the windows. Unfortunately AHK is not Visual basic ( :headwall: ) But there are still a lot of things that can be done. The only real problem in asthetic values are once you decide one way it affects the look "feel" and over all appeal of the program... That being said I am sure I'll come up with something appealing.

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
#SingleInstance force ; this prevents the script from popping up the dialogue box warning if you start the program without shutting it down.
DetectHiddenWindows, On ; unnecessary but left just in case of future changes.

Global Cache:=% A_WorkingDir "\Cache"
if !FileExist(Cache)
	FileCreateDir, % Cache
Global Temp:=% Cache "\Temp.txt"
Global TempBack:=% Cache "\Tem.txt"
rawtext:= DesGet()
Global Speller:= RawTextFix(rawtext)

CoordMode, Caret, Screen
CoordMode, Mouse, Screen
Gui, AI:New, +AlwaysOnTop +Resize +toolWindow
Gui, Add, Edit, r2 ym x100 vWords w200 hwndTracker, this is a long string of words to use. It is to help in getting a large amount of words to test in the possibility of edit and editing controls.
Gui, Margin, 50, 20
Gui, Show, , AI work
Gui, Words:New, +AlwaysOnTop -SysMenu -Caption +toolWindow
Gui, Add, Listbox, r6 gWord vWord x0 y0,
Gui, margin, 0, 0
Gui, Show, , Words
WinSet, Transparent, 0, Words
WinActivate, AI

historyarray:= []
ustring:= []

Loop,
{
	winwonder:=WinWonder()
	if (winwonder == "False")
	{
		Sleep, 500
		Continue
	}
	userinput:= GetControl("Text")
	history:= GetHistory()
	if (userinput == history)
	{
		Sleep, 500
		Continue
	}
	historyarray:= Ret_Arr(history)
	ustring:= Ret_Arr(userinput)
	nstring:= ReturnDif(historyarray, ustring)
	if (nstring == "NotFound")
		MsgBox, The text could not be found!
	usmiss:= ustring["wordPos" nstring]
	usfix:= historyarray["wordPos" nstring]
	wefixed:= RegExReplace(usmiss, usfix, "")
	ourlength:= StrLen(wefixed)
	if (ourlength < 2)
	{
		Sleep, 500
		Continue
	}
	if (ourlength > 3)
	{
		ourwords:=WordCheck(wefixed, ourlength)
		if (ourwords == "")
			Continue
		SpellChecker()
		GetControl(ourwords)
	}
}
Return

Word:
Return

WordCheck(uword, str)
{
	Sort, Speller, U D|
	Loop, Parse, % Speller, |
	{
		mword:= SubStr(A_LoopField, 1, str)
		if (mword == uword)
		{
			z++
			IfNotInstring, ourlist, %A_LoopField%
			{
				if (z == 1)
					ourlist:= % ourlist A_LoopField "||"
				else if (z != 1)
					ourlist:= % ourlist A_LoopField "|"
			}
		}
	}
	if (ourlist == "")
		Return
	Return % ourlist
}

SpellChecker()
{
	WinActivate, AI
	Y:= (A_CaretY+11)
	X:= (A_CaretX-27)
	WinGetPos, WX, WY, , , Words
	WinGet, visible, Transparent, Words
	if ((visible != 0)&&(WY != Y))
			WinMove, Words, , % X, % Y
	if (visible == 0)
	{
		WinSet, AlwaysOnTop, Off, AI
		WinSet, Transparent, 200, Words
		WinMove, Words, , % X, % Y
		winwonder:=WinWonder()
		if (winwonder == "False")
			WinActivate, AI
	}
	Return
}

ReturnDif(harray, uarray)
{
	Loop
	{
		x++
		uinput:= uarray["wordPos" x]
		hinput:= harray["wordPos" x]
		if (uinput == "")
			Return % x
		if (uinput != hinput)
			Return % x
	}
	Return
}

CheckIt(user, y, nstring)
{
	Loop, Parse, % user, % A_Space
	{
		x++
		if (x == y)
			if (A_LoopField == nstring)
				Return "True"
	}
}

Ret_Arr(text)
{
	array:= []
	Loop, Parse, % text, % A_Space
	{
		x++
		array["wordPos" x]:= A_LoopField
	}
	Return % array
}

GetHistory()
{
	FileRead, contents, % Temp
	Return % contents
}

GetControl(FocTex)
{
	GuiControlGet, InFocus, AI:Focus
	GuiControlGet, text, AI:, % InFocus
	GuiControlGet, Word, Words:, Listbox1
	if (FocTex == "Focus")
		Return % InFocus
	if (FocTex == "Text")
		Return % text
	if (FocTex == "Word")
		Return % Word
	GuiControl, Words:, Listbox1, | 
	GuiControl, Words:, Listbox1, % FocTex
	Return
}

WinWonder()
{
	IfWinActive, AI
		Return % "True"
	IfWinNotActive, AI
		Return % "False"
}

RawTextFix(string) ; a series of string replace commands to eliminate special characters and so on. 
{
	StringReplace, string, string, (*Inner thought[lmn]) (, , All
	StringReplace, string, string, {*What I'm doing[lmn]} {, , All
	StringReplace, string, string, Chris Says:  , , All
	StringReplace, string, string, (*Chris thinks[lmn]) ( , , All
	StringReplace, string, string, {*Chris is doing[lmn]} { , , All
	StringReplace, string, string, (*Luke thinks[lmn])  (, , All
	StringReplace, string, string, {*Luke is doing[lmn]} {, , All
	StringReplace, string, string, *[10], , All
	StringReplace, string, string, "[10], , All ; " <--- this is being done because of my edited language encoder for AHK.
	StringReplace, string, string, }[10], , All
	StringReplace, string, string, )[10], , All
	StringReplace, string, string, `n`r, , All
	StringReplace, string, string, `n, , All
	StringReplace, string, string, `r, , All
	var:= SubStr(string, 1, 1) ; for beginning of string 
	if (var == "*")
	{
		str:=StrLen(string)
		string:=SubStr(string, 2, str)
	}
	nv = " ; " <--- this is being done because of my edited language encoder for AHK.
	if (var == nv)
	{
		str:=StrLen(string)
		string:= SubStr(string, 2, str)
	}
	StringReplace, string, string, ., % A_Space, All
	StringReplace, string, string, |, % A_Space, All
	StringReplace, string, string, !, % A_Space, All
	StringReplace, string, string, ?, % A_Space, All
	StringReplace, string, string, ", % A_Space, All ; " <--- this is being done because of my edited language encoder for AHK.
	StringReplace, string, string, *, % A_Space, All
	StringReplace, string, string, `,, % A_Space, All
	StringReplace, string, string, % A_Space A_Space, % A_Space, All
	StringReplace, string, string, % A_Space, |, All
	StringReplace, string, string, `n, |, All
	Return % string
}

DesGet()
{
	FileRead, descriptions, % Cache "\descriptions.txt"
	if Not ErrorLevel
	{
		Loop, Parse, % descriptions, `n
		{
			if (A_LoopField == "")
				Continue
			IfNotInstring, list, % A_LoopField
				list:=% list A_LoopField "|"
		}
		descriptions:=% list
	}
	if (ErrorLevel == 1)
	{
		MsgBox, % "the file does not exist in current directory:`n" Cache "\descriptions.txt`n please place your words in the above location`n FATAL ERROR 1 FILE DOES NOT EXIST"
		exitapp
	}
	Return % descriptions
}

AIGuiClose:
esc::
exitapp

l6908lay
Posts: 43
Joined: 09 Apr 2023, 05:16

Re: I'll call this version v0.0.9 of Helper tool.

Post by l6908lay » 01 Dec 2023, 06:32

I have realized something with very large text and EOL (like `n, `r, CR+LF). if you are gaining mismatched information such as This array doesn't equal that array or this contents doesn't equal that contents. Loop Parse tends to leave in the EOL on the end of the line from a file read... To Example:

Code: Select all

FileRead, contents, filename.location
Loop, Parse, % contents, `n ; or whatever you parse by.
{
	x++
	array["here" x]:= A_LoopField ; <-------------------------- that that right there will actually still have the EOL sometimes.
}
In order to fix it:

Code: Select all

FileRead, contents, filename.location
Loop, Parse, % contents, `n ; or whatever you parse by.
{
	x++
	y:=StrLen(A_LoopField)
	y--
	newstr:= SubStr(A_LoopField, 1, y) ;<------------- substring it off.
	array["here" x]:= newstr
}

User avatar
boiler
Posts: 17212
Joined: 21 Dec 2014, 02:44

Re: I'll call this version v0.0.9 of Helper tool.

Post by boiler » 01 Dec 2023, 07:20

There is a simpler fix for that which is pretty standard for AHK scripts, as shown in Example #2 of theLoop, Parse documentation page. Just specify `r for the OmitChars parameter.

Code: Select all

Loop, Parse, FileContents, `n, `r

Note that you don’t need a % to force an expression for the InputVar parameter because it is expecting a variable for that parameter.

l6908lay
Posts: 43
Joined: 09 Apr 2023, 05:16

Re: I'll call this version v0.0.9 of Helper tool.

Post by l6908lay » 01 Dec 2023, 13:30

Hmm I see. But it's so weird because I string replaced off all occurences of that actually. So I wouldn't have every even imagined that `r still existed. But thank you for the much more elegant answer.

l6908lay
Posts: 43
Joined: 09 Apr 2023, 05:16

Re: I'll call this version v0.0.9 of Helper tool.

Post by l6908lay » 01 Dec 2023, 14:18

boiler wrote:
01 Dec 2023, 07:20
There is a simpler fix for that which is pretty standard for AHK scripts, as shown in Example #2 of theLoop, Parse documentation page. Just specify `r for the OmitChars parameter.

Code: Select all

Loop, Parse, FileContents, `n, `r

Note that you don’t need a % to force an expression for the InputVar parameter because it is expecting a variable for that parameter.
Unfortunately after attempting the metioned fix I have found that it still does not work.
Capture.PNG
Capture.PNG (25.61 KiB) Viewed 1504 times
The code the message box to show the differences

User avatar
boiler
Posts: 17212
Joined: 21 Dec 2014, 02:44

Re: A helpful editing tool.

Post by boiler » 01 Dec 2023, 17:27

That method always works. It's hard to determine why your script is not doing what you are not expecting when you are showing only part of the script in that image and not isolating the parsing loop itself. We don't see the MsgBox line. We don't see why it would say "this is the new line" in the middle of it. You should post a complete and standalone script that just reads the text and performs the parsing and exhibits the issue you describe. The issue you are seeing is almost certainly happening somewhere else in your script. The parsing loop would not produce resulting strings that contain any LF (`n) characters, and there would be no CR (`r) characters unless there were some other than at the beginning or end of a parsed line (i.e, not part of a CR+LF pairing).

What I think what may be happening is that some of the elements of the array you are building are ending up blank/null. And you have a loop that builds the text to display in the MsgBox and it places each one on a new line. So if there is an element that contains nothing, your method for building the display shows "nothing" on its own line, making you think there is a line break in what was parsed out. Again, if you show everything that generates that, we can tell you exactly why it's happening.

And by the way, you don't need % in either place shown in that image. The one before "lines" isn't needed because it's expecting a variable for that parameter. The one before "linearray" isn't needed because the parameter for Return is already expected to be an expression.

l6908lay
Posts: 43
Joined: 09 Apr 2023, 05:16

Re: A helpful editing tool.

Post by l6908lay » 01 Dec 2023, 18:43

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
#SingleInstance force ; this prevents the script from popping up the dialogue box warning if you start the program without shutting it down.
#MaxMem
DetectHiddenWindows, On ; unnecessary but left just in case of future changes.

FileAppend, % "`nThis is my current string with a softreturn`n", test.txt, UTF-8 ; the beginning seems to come with a hard cr+lf return
FileAppend,
(
`n"\|/Softreturn\|/"this text should not appear line1"\|/Softreturn\|/"`n ; this line will not appear
`n`nthis text should not appear line2`n`n ; this line will appear.

A line of text.
By default, the hard carriage return (Enter) between the previous line and this one will be written to the file.
    This line is indented with a tab; by default, that tab will also be written to the file.
Variable references such as %Var% are expanded by default.
), test.txt
FileRead, contents, test.txt
StringReplace, contents, contents, "\|/Softreturn\|/", `n, ALL
Loop, Parse, % contents, `n
	IfInString, A_Loopfield, `r
		MsgBox, Found in string:`n %A_Loopfield%
exitapp
please take a moment to try the following above code. the soft return `n is completely ignored and shows n the message box this may be because of beginning string error. the second line (programically changed) will not appear which I named as line 1. and line 2 shows even though it is wrapped in `n`n "words" `n`n <---- that should not happen@boiler

User avatar
boiler
Posts: 17212
Joined: 21 Dec 2014, 02:44

Re: A helpful editing tool.

Post by boiler » 01 Dec 2023, 20:17

I don't understand why you are supposedly trying to prove that using the parsing loop with the OmitChars parameter doesn't work, and you're asking me to follow a script that doesn't even use that approach. What does that script have to do with what I recommended and whether it works or not?

l6908lay
Posts: 43
Joined: 09 Apr 2023, 05:16

Re: A helpful editing tool.

Post by l6908lay » 01 Dec 2023, 20:30

Well to be completely honest... some users may choose to use the soft return over the hard return in some situations. This creates a "cute" way of typing things on certain other applications. By changing the value of a hard return or soft return you change the dynamic of how text is read or interpretted... But even more than that the "Edit box" on uses `n (either windows design or ahk IDK). But if a user were to upload a document that carries a mix of both (or just one type) that dynamically will change their script. For instance, hard returns in excel create a line break between cells while soft returns create a line break within a cell (kinda like putting enter in a cell verses creating a whole new cell). Of course there are many other differences used in a variety of other applications for a soft and hard return send. If a user is attempting to FileRead a file to send that file via keyboard command it comes with the caveate of creating the wrong values of end of lines.

The purpose of the script is to prove that `n and cr+lf is not considered two different keyboard gestures in FileRead (or maybe FileAppend? or maybe both).

l6908lay
Posts: 43
Joined: 09 Apr 2023, 05:16

Re: A helpful editing tool.

Post by l6908lay » 01 Dec 2023, 23:01

And for anyone who might be reading the "CR+LF" is in `r`n direction not `n`r direction. Which can also cause problems when attempting to read a file (residual `r's and/ `n's that might exist).

User avatar
boiler
Posts: 17212
Joined: 21 Dec 2014, 02:44

Re: A helpful editing tool.

Post by boiler » 01 Dec 2023, 23:53

Anyone who might be reading should know it causes no problems if you just handle it correctly.

If your file has CR+LF between where you want to break those lines and you want to preserve where there are lone LF characters, then split the lines at every CR+LF combination, and you’ll be fine. Other than that rare case, just parse it like I said and the documentation shows.

l6908lay
Posts: 43
Joined: 09 Apr 2023, 05:16

Re: A helpful editing tool.

Post by l6908lay » 02 Dec 2023, 00:07

Capture.PNG
Capture.PNG (152.35 KiB) Viewed 1381 times
it works perfectly in that direction
Capture2.PNG
Capture2.PNG (116.62 KiB) Viewed 1381 times
and that direction is horrible.

User avatar
boiler
Posts: 17212
Joined: 21 Dec 2014, 02:44

Re: A helpful editing tool.

Post by boiler » 02 Dec 2023, 00:09

It's as simple as this:

Code: Select all

Lines := StrSplit(FileText, "`r`n")
Then every element of the array Lines will retain lone LF characters, which you are referring to as soft line breaks.

l6908lay
Posts: 43
Joined: 09 Apr 2023, 05:16

Re: A helpful editing tool.

Post by l6908lay » 02 Dec 2023, 00:19

The soft line break is a `n which is procduced by +{enter} or shift+enter. the hard line is done by a {enter} "`r`n." StrSplit would probably produce an array of lines but still doesn't solve the +{enter} vs. the {enter}... I think I'll just have to create a (on-shift enter) hotkey that rewrites the script each time enter is pressed to retain which enter was pressed... With of course atoggle switch to activate or turn off... Similar to what i did with the tab key.

User avatar
boiler
Posts: 17212
Joined: 21 Dec 2014, 02:44

Re: A helpful editing tool.

Post by boiler » 02 Dec 2023, 00:26

No, I just told you how it solves it. It breaks it at every `r`n, leaving the lone `n characters in place. It’s like you’re not reading what I say or refusing to believe it. You seem intent on making it more complicated than it is.

Post Reply

Return to “Scripts and Functions (v1)”