How to find all globals in routines?
The task is to find all globals that are referenced in certain routines. I could search for ^ using
The Find and Replace screen in Studio has a capability of searching for ^ with Match Element Type set to Global Variable. Maybe I could use that but what is the ObjectScript function behind this screen if any?
Product version: IRIS 2022.1
With the help of a standard editor only - that will be a difficult and cumbersome task, for example, in the below snippet try to find the "^Test" global (not the routine ^Test):
set value = ^Test // take a value from the global ^Test do ^Test // call the routine ^Test // a more complex case // set ref = "^Test" // in this case you are lucky set ref = "^" _ "Test" // this could be a stumbling stone // few lines later set value = @ref // get the value of ^Test global do @ref // call the routine ^Test
You will need some kind of an "intelligent" editor which can make a difference between a call (do) like operation and a "get-value" like operation. The one I know of (but I have never used it, so I don't know how good it works) is the RE/parser of GJS. Asking Google is a good starting point.
Thanks, Julius
I recommend using visual studio code (vs-code) where you can search with regex. searching. Consider also seqdhing for 0-n while spaces to elimnate all spaces, tabs etc.
for example: a reference to a global could be:
set ^global( ... )=
s ^global( ... )=
s:cond ^global( ... )=
If combined with other commands: then you should search for the comma (,) e.g.
set var=someting,^global( ...)=
However, use of indirection is very complex to find... (you need to skip 0-n of any characters including new lines between the set and the use of @)
Thanks, Yaron. I'll skip looking for indirections for now.
And do not forget, if the application has/uses parts of "older code" then the so called "naked syntax" may also be a issue (of course not, if you just want to know the name of the global).
Classmethod Test() { kill ^myGlobal kill ^yourGlobal set ^myGlobal(2)="some data" do ..moreData("data1") set ^yourGlobal(3)="other data" do ..moreData("data2") // Now, the globals look like // // ^myGlobal(2)="some data" // ^myGlobal(9)="data1" // // ^yourGlobal(3)="other data" // ^yourGlobal(9)="data2" } ClassMethod moreData(data) { set ^(9)=data }
Beside all the "nice" combinations of direct sets, indirections, naked synates etc. do not forget, your application may call routinies/methods which are in deployed mode (third party APIs and utilities - hopefully with documentation)
@Anna Golitsyna If you goal is to find all globals referenced in a document, you can use a modified version of the code I included in this comment. The code uses the %SyntaxColor class to get a JSON array of semantic tokens for a document, and then loops through them looking for global references. Note that this will only find literal references, not naked references or indirection.
ClassMethod WriteAllGrefs() { Set syn = ##class(%SyntaxColor).%New(), in = ##class(%Stream.TmpCharacter).%New(), out = ##class(%Stream.TmpCharacter).%New() #; TODO Put your document's contents into "in" Do syn.Color(in,out,"COS" /* or "INT" or "CLS" */,"KE" /* K means JSON output, E means keep empty lines */) #; Format of the JSON output: #; [ #; #; One array for each source line #; [ #; { #; #; Language of the token. See Languages() in %Library.SyntaxColor. #; "l": %Integer, #; #; Attribute of the token. See Attributes() in %Library.SyntaxColor. #; "s": %Integer, #; #; Zero-indexed start position of the token on the line #; "p": %Integer, #; #; Length of the token in characters #; "c": %Integer #; } #; ] #; ] Set json = ##class(%DynamicArray).%FromJSON(out), lineIter = json.%GetIterator() While lineIter.%GetNext(.lineNum,.lineTokens) { Set tokensIter = lineTokens.%GetIterator() While tokensIter.%GetNext(,.token) { If ( #; COS (token.l = 1) && #; Global reference (token.s = 18) ) { Write "Gref starting in column ",token.p + 1," of line ",lineNum + 1,! } } } }
You can combine this with the %Library.RoutineMgr_StudioOpenDialog query to make an index of all globals referenced in a subset of documents.
Thanks, Brett. I did try using SyntaxColor but not with JSON and did not like the HTML results. I'll give your code a try.
Excellent, just excellent. It did find correctly all globals in a routine (no indirection). I populated in = ##class(%Stream.TmpCharacter).%New() with this code: ; Omit extension
S NumLines=^ROUTINE(routineName,0,0)
F n=0:1:NumLines {
S line=$T(@routineName+n^@routineName)
D in.Write(line),in.Write($c(13,10))
}
D in.Rewind()
I also added this after your Write:
Set line=$G(^ROUTINE(rtnName,0,lineNum+1))
Write $C(9),$E(line,token.p+1,token.p+token.c),!
I did not try finding globals in classes, but I assume this would be very similar. Is there any online documentation that shows that token value for globals is 18? Would be curious about other token values.
I'm glad you find this useful! Instead of looping through ^ROUTINE or ^rMAC to build the stream, you can open a %Routine object and pass that in as the first argument. That class implements the Stream interface. You can get a list of languages and attributes per language from a %SyntaxColor object using the Languages() and Attributes() methods.
I have an odd problem. In vast majority of cases globals are defined by your code correctly. However, for some routines the lineNum as returned by GetNext is one line count off and the global with the stated token attributes is actually on the next line. I tried to slightly modify such routines and recompile, but it did not always help. Any advice?
Can you give me an code snippet?
I will have time to prepare such a snippet tomorrow, sorry. Meanwhile, it's not necessarily one line off but up to several lines off, a few routines only. In your code, is it possible to extract text of the line for which tokens are calculated? I looked around and did not see such a possibility.
I just messaged you two snippets. Thanks again.
The issue is that I forgot to add the E/e flag to the list of flags for the Color() method, so empty lines were removed from the JSON output. I will edit the accepted answer.
Yes, the problem is gone! Thanks a lot for your help.
💡 This question is considered a Key Question. More details here.