lexikos wrote:But even so, having several different string functions which overlap with SubStr could make it easier to both write and read scripts. The author doesn't have to think about the correct way to calculate the index and length, and the reader doesn't have to interpret the expression to determine which part of the string it's getting.
That's incredibly encouraging to hear you saying something like this, could have been me saying that.
lexikos wrote:I don't know about hacks, but there's a clean solution: use a separate compatibility library for each version.
Yes, I think that's the best option too. The issue boils down to: is it possible to have a custom function refer to a built-in function with the same name.
lexikos wrote:You don't know just how close I've come to abandoning AutoHotkey.
We love AutoHotkey, however long things take, we're all behind it.
==================================================
@nnnik. Re. #Include. I think the best solution is something like (although I wouldn't mind a different name) #IncludeIfParamContains(Path, ParamNum, Needle), so it checks if Parameter N contains the needle, and if it does, then the script is included. I believe this solves all my conceivable problems regarding 'conditional #Include'. A script sets the parameter before launching.
Re. GUI, let's say I've already created a GUI window via DllCall, e.g. with a custom class name and no icon. Can the Gui command 'take over' that window as though it was created by the Gui command in the normal way.
Also, with InputBox, is it possible to binary edit the dialog resource in the AutoHotkey exe file, to give it a larger (or different) font, in my efforts so far, the InputBox became bigger, but the font remained the same.
MsgBox/InputBox gets hidden under another window (+ InputBox custom font) - Page 2 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 37#p138537
==================================================
The string functions that I would have in AHK v2 (or also AHK v1):
- StrTrimLeft/StrTrimRight/StrLeft/StrRight(vText, vNum) [function versions of String(Trim)Left/Right]
- StrIn/StrContains/StrStarts/StrEnds(vText, vNeedles, vDelim:=",", vCaseSen:=0, ByRef vMatch:="") [function versions of 'if var in/contains']
- StrRept(vText, vNum)
- StrCountOcc(vText, vNeedle, vCaseSen:=0) [or 'StrCount']
I might consider the following, although I would be happy to do these as my own custom functions, as the usage is rarer:
StrLeft(vText, vNum, vParam3) ;get the first vNum characters, then the last vParam3 of those
StrRight(vText, vNum, vParam3) ;get the last vNum characters, then the first vParam3 of those
I have hundreds of string functions, so please believe me that I've been very sparing in considering what functions I would suggest for AHK v1/v2.
Some advantages:
- StrStarts/StrEnds are much faster than RegExMatch and don't have issues with escaping characters
- turning InStr into StrContains, to add another item, is far easier than changing it to 'if var contains'
- in general StrIn/Contains/Starts/Ends and InStr are far more interchangeable than what is available currently
- without StrRept, you have to 'stop what you're doing' and use a loop, before continuing with the code
- using StrCountOcc is far more readable, than its equivalent involving StrReplace
- string functions are fundamental and can be asked to handle hundreds of megabytes of data, I do this several times a week, optimisation as a built-in function/command is desirable
- I often waste time thinking: I can do SubStr either future-proof and ugly, or looking reasonable but requiring subsequent conversion, can I get away with RTrim here, RegExReplace might work here but it's slow and might confuse the user and seems overkill plus what if the string contains special characters, I could use Format, will this method be slow on big text ...
- if InStr/SubStr were changed in AHK v2 to make then more intuitive, then intuitiveness is a reasonable argument in relation to other string functions
- people see the 'String' commands as holdovers from AutoIt, without realising their potential as demonstrated by the 'StrReplace' and 'StrSplit' revamps
- easier to convert code from other languages that use functions similar to StrLeft/Right and StrRept
- easier to share code on forums without defining commonly-needed string functions at the bottom
- with StrEnds v. multiple InStr, you don't have to work out the string length each time
- once you've conceived of these functions, you will come across situations where you wish they were already built-in functions
- code is more readable and understandable, and easier to maintain
[EDIT:]
- there are times when you want to write stand-alone functions, that make no reference to other functions, and so must use ugly and lengthy techniques like InStr a dozen times, rather than RegExMatch which is shorter but slows down the script, in such cases the nature of the available string functions becomes more important
- there are potential disadvantages in using custom functions to perform core duties, versus built-in functions
- 'if var contains/in' has limitations relating to using 'if' with '&&' and '||' and other conditions in adjacent lines, and can thus mislead the person reading/maintaining the script regarding control flow, also, it affects indentation
E.g. code for StrStarts, StrRept, StrCountOcc:
Code: Select all
;e.g. JEE_StrStarts(vText, "red,yellow,green,blue")
;e.g. JEE_StrStarts(vText, "red,yellow,green,blue", ",") ;same as line above
;e.g. JEE_StrStarts(vText, "red|yellow|green|blue", "|")
;e.g. JEE_StrStarts(vText, "Return,", "") ;treat as one string, in a similar way to InStr
;e.g. JEE_StrStarts(vText, "Return,,", ",,") ;'if var contains'-style double comma handling
JEE_StrStarts(ByRef vText, ByRef vNeedles, vDelim=",", vCaseSen=0, ByRef vMatch="")
{
vSCS := A_StringCaseSense
StringCaseSense, % vCaseSen ? "On" : "Off"
if (vDelim = "")
{
StringCaseSense, % vSCS
Return !!(SubStr(vText, 1, StrLen(vNeedles)) = "" vNeedles)
}
if (vDelim = ",,")
{
vDelim := JEE_StrGetUnusedChar1Var(vText)
vText := StrReplace(vText, ",", vDelim)
vText := StrReplace(vText, vDelim vDelim, ",")
}
Loop, Parse, vNeedles, % vDelim
if !(SubStr(vText, 1, StrLen(A_LoopField)) <> "" A_LoopField) ;if not different (i.e. if same) (handles case sensitive/insensitive)
{
vMatch := A_LoopField
StringCaseSense, % vSCS
Return 1
}
StringCaseSense, % vSCS
Return 0
}
;==================================================
JEE_StrRept(vText, vNum)
{
if (vNum <= 0)
Return ""
VarSetCapacity(vOutput, StrLen(vText)*vNum*(A_IsUnicode+1))
Loop, % vNum
vOutput .= vText
Return vOutput
}
;==================================================
JEE_StrCountOcc(ByRef vText, vNeedle, vCaseSen=0)
{
if (vNeedle = "")
Return "" ;StrReplace's count returns 0, but perhaps this should return blank
vSCS := A_StringCaseSense
StringCaseSense, % vCaseSen ? "On" : "Off"
StrReplace(vText, vNeedle, "", vCount)
;StrReplace(vText, vNeedle, vNeedle, vCount) ;seemed to be slower than the line above
;note: using InStr and Loop together seemed very slow
StringCaseSense, % vSCS
Return vCount
}
==================================================
Re. 'StrRight' and 'StrEnds'. I recently collected some examples regarding the ends of strings:
Code: Select all
if (SubStr(vText, StrLen(vText)-StrLen(vSuffix1)) = vSuffix1)
|| (SubStr(vText, StrLen(vText)-StrLen(vSuffix2)) = vSuffix2)
|| (SubStr(vText, StrLen(vText)-StrLen(vSuffix3)) = vSuffix3)
;AHK v1 only
if (SubStr(vText, 1-StrLen(vSuffix1)) = vSuffix1)
|| (SubStr(vText, 1-StrLen(vSuffix2)) = vSuffix2)
|| (SubStr(vText, 1-StrLen(vSuffix3)) = vSuffix3)
;AHK v2 only
if (SubStr(vText, -StrLen(vSuffix1)) = vSuffix1)
|| (SubStr(vText, -StrLen(vSuffix2)) = vSuffix2)
|| (SubStr(vText, -StrLen(vSuffix3)) = vSuffix3)
vIsV1 := !!SubStr(1,0)
if (SubStr(vText, vIsV1-StrLen(vSuffix1)) = vSuffix1)
|| (SubStr(vText, vIsV1-StrLen(vSuffix2)) = vSuffix2)
|| (SubStr(vText, vIsV1-StrLen(vSuffix3)) = vSuffix3)
if (StrRight(vText, StrLen(vSuffix1)) = vSuffix1)
|| (StrRight(vText, StrLen(vSuffix2)) = vSuffix2)
|| (StrRight(vText, StrLen(vSuffix3)) = vSuffix3)
;introducing StrEnds:
;3rd parameter: ',' for lists, ',,' for 'if var contains' style commas,
;blank for treat commas literally, another character for an alternative delimiter
;if it is not known whether vSuffix1/2/3 contains commas or not
if StrEnds(vText, vSuffix1, "")
|| StrEnds(vText, vSuffix2, "")
|| StrEnds(vText, vSuffix3, "")
;assuming there are no problem characters or pipes
;and bearing in mind that RegExMatch can be slow
if RegExMatch(vText, "(" vSuffix1 "|" vSuffix2 "|" vSuffix3 ")$")
;if vSuffix1/2/3 are known not to contain commas
if StrEnds(vText, vSuffix1 "," vSuffix2 "," vSuffix3)
;if vSuffix1/2/3 are known not to contain pipes
if StrEnds(vText, vSuffix1 "|" vSuffix2 "|" vSuffix3, "|")
This function to add indentation to code, has examples where I would have used StrRept and StrEnds, but commented them out, to avoid references to custom functions:
text/list/table functions (latest: add indentation, get nth(-to-last) line of specified length, html to/from text) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 63#p134363
Code: Select all
;if !vType && StrStarts(vTemp, vList)
vList := StrReplace(vList, ",", "|")
if !vType && RegExMatch(vTemp, "i)^(" vList ")")
;vOutput .= StrRept("`t", vCount+vAdjNow) vTemp "`r`n"
Loop, % vCount+vAdjNow
vOutput .= "`t"
vOutput .= vTemp "`r`n"
==================================================