Question
· Feb 4

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  class(%Studio.Project).FindInFiles  but that would also find ^ in comments and function calls. I can distinguish between a global and a function call visually, but it would be lovely to be able to skip function calls programmatically. Is it possible?

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
Discussion (15)3
Log in or sign up to continue

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.

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 @)

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.

Excellent, just excellent. It did find correctly all globals in a routine (no indirection). I populated in = ##class(%Stream.TmpCharacter).%New() with this code:
NumLines=^ROUTINE(routineName,0,0)   ; Omit extension
n=0:1:NumLines {
    S line=$T(@routineName+n^@routineName)
    D in.Write(line),in.Write($c(13,10))
}
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.