How to Write Edit Macros


by Howard Fosdick   © 2024 RexxInfo.org

Introduction

Ever wish you could tailor the behavior of your text editor to your own preferences? Or make it easier or quicker to use?

Edit macros allow you to do exactly that.

An edit macro is a Rexx script that you run that affects or drives the behavior of your text editor. It can make it easier for you to:

    • Quickly perform operations
    • Execute repeated tasks
    • Automate routine or repetitive tasks
    • Simplify your use of the editor
    • Customize or tailor or extend your editor

This article explains how to write Rexx edit macros to work with common editors. We’ll explain the coding requirements for macros and walk through a couple examples.

Whether you can write an edit macro for any particular editor depends on whether that editor supports this feature. Some common text editors you can write Rexx edit macros for include:

    • ISPF – available on most mainframes
    • Xedit – a mainframe editor widely used on z/VM
    • THE – freely available for Windows, Linux, Unix, BSD, and many other platforms
    • KEDIT – a commercial Windows editor

ISPF Editor Macros

Let’s explore how to write Rexx edit macros for the editor available on most mainframes, ISPF. To simply the discussion, we’ll assume you’re on a z/OS system using TSO/E.

You typically create your Rexx macro as a member of a partitioned dataset. They can reside in any of several concatenations, normally SYSPROC, SYSUPROC, SYSEXEC, SYSUEXEC, or an activated ALTLIB.

The name of the dataset you create is your macro’s name. The first line of code should contain the word REXX inside a comment, such as: /* REXX */

The second statement in the macro should direct commands to the edit processor: ADDRESS “ISREDIT”

Remember that otherwise Rexx sends commands to the default command environment (TSO on z/OS systems).

You can look at an edit macro as a subcommand to the ISPF editor. It’s executed just as if it were an editor subcommand. To run it, you just enter its name on the editor’s command line.

A macro can contain these statements:

    • Edit subcommands
    • Other edit macro commands
    • TSO commands
    • Rexx statements
    • ISPF and PDF dialog service requests

A Simple Example

Here’s a simple example. This macro named ONLY from Willy Jensen displays only lines containing specific content. The user inputs what that string content is as a command line parameter when he runs the macro. Here’s the code:


  /* rexx     by Willy Jensen
    Show only records with specified content
  */
   Address isredit "MACRO (PRM) NOPROCESS"
   Address isredit
   "reset"
   "x all"
   "find all" prm
   exit 0

As required, the macro starts with a first line comment containing the word rexx. That's not case-sensitive.

The first executable line of code is the Address isredit statement. It captures the user’s input parameter into the variable PRM. The NOPROCESS parameter simply means that the macro will be executed after all ISPF edit line commands are processed.

The second Address isredit statement has no command associated with it. So, that tells the processor to direct all subsequent commands in the script to the editor (rather than the default command environment of TSO/E).

Editor commands reset and x all remove all messages and lines from view in the data viewing area.

The command "find all" prm then completes the program’s task. It ensures that all the lines containing the string in the prm variable appear in the data area. So the macro exits.


Another Example

Here’s another useful macro from Willy Jensen. This one is called UNIQUE. It deletes any duplicate records from the dataset you’re editing:


     /*       rexx   by Willy Jensen
      Unique
      Edit macro - delete duplicate lines
     */

 	Address Isredit "MACRO NOPROCESS (PRM)"
 	Address Isredit
 	sf.=''
 	"reset"

 	/* scan data */
 	"(lines) = linenum .zl"
 	dn=0
 	Do lnr=1 to lines
   	  "(s) = Line (lnr)"
   	  s='a'strip(s,'t')
   	     if sf.s<>'' then do
     	    "label" lnr "= .xdup"
           "flip .xdup"
           dn=dn+1
         end
	  else sf.s=lnr
   	End
 	"delete all x"

 	exit xmsg(dn 'lines deleted')

	XMSG:
 	parse arg zedlmsg
 	address ispexec"setmsg msg(isrz000)"
 	return 0 

This macro starts with the same three lines as the previous example. These declare that it’s written in Rexx, that it’s an edit macro, and that all subsequent lines Rexx passes to an outside environment for execution will go to the editor rather than to TSO.

The program logic starts after the /* scan data */ comment. The number of lines we’re processing is assigned to the variable lines. This value controls the DO loop, as the loop processes one line at a time.

Inside the loop, you see a clever use of an associative array to see if a line is a duplicate that has been processed previously. This logic is directed at the compound variable (array) named sf.. (To learn more about arrays in Rexx, see this article.)

If a line is determined to be a duplicate, this command assigns it a label as such: "label" lnr "= .xdup"

In this case, this statement increments the counter of duplicated lines: dn=dn+1

This command completes the processing by deleting all the lines marked as duplicated: "delete all x"


A Last Example

Sometimes it’s educational to review code written by different people to get an idea of stylistic differences. So here’s an ISPF macro example from a different source.

This final example is straight from the IBM z/OS manual, ISPF Edit and Edit Macros. That’s the essential reference manual for learning more about how to write edit macros. Along with background information, that manual lists and defines all edit line commands, primary commands, macro commands, and assignment statements. (You can download it along with all other Rexx-related IBM manuals from here.)

This example program is named ISRMBRS. It enables you to make global changes to all members in a partitioned data set. Or you could use it to search all PDS members for a specific data string.

The way it works is that you start this macro and pass to it the name of the or macro you want to run against all PDS members. Then it turns around and applies that macro to each member of the PDS.

You invoke this macro from the editor command line as: ISRMBRS macname

macname is the name of the macro you want run against each PDS member.

Here’s the program:


/*REXX****************************************************************/
/* ISPF edit macro to process all members of partitioned data set,   */
/* running a second, user-specified, ISPF edit macro against each    */
/* member.                                                           */
/*                                                                   */
/* To run:                                                           */
/* Enter "ISRMBRS macname" on the command line, where macname is     */
/* the macro you want run against each member.                       */
/*********************************************************************/

’ISREDIT MACRO (NESTMAC)’

/*********************************************************************/
/* Get dataid for data set and issue LMOPEN                          */
/*********************************************************************/
’ISREDIT (DATA1) = DATAID’
’ISREDIT (CURMEM) = MEMBER’
Address ispexec ’LMOPEN DATAID(’data1’) OPTION(INPUT)’
member = ’ ’
lmrc = 0

/*********************************************************************/
/* Loop through all members in the PDS, issuing the EDIT service for */
/* each. The macro specified on the ALLMEMS invocation is passed as  */
/* an initial macro on the EDIT service call.                        */
/*********************************************************************/
Do While lmrc = 0
  Address ispexec ’LMMLIST DATAID(’data1’) OPTION(LIST),
                  MEMBER(MEMBER) STATS(NO)’
  lmrc = rc
  If lmrc = 0 & member ^= curmem Then
    do
      Say ’Processing member’ member
      Address ispexec ’EDIT DATAID(’data1’) MEMBER(’member’)
                      MACRO(’nestmac’)’
    end
End

/*********************************************************************/
/* Free the member list and close the dataid for the PDS.            */
/*********************************************************************/
Address ispexec ’LMMLIST DATAID(’data1’) OPTION(FREE)’
Address ispexec ’LMCLOSE DATAID(’data1’)’

Exit 0

To explain the program, it starts with the keyword REXX in its first comment line, and with the ISREDIT MACRO statement in its first executable line.

Next, the first code block gathers dataset information and issues an LMOPEN to access it. The program uses the ispexec statement to invoke ISPF services (as opposed to ISREDIT, which you use to identify edit macros).

The next Address ispexec statement invokes LMMLIST, another ISPF service. It obtains the names of PDS members.

With the PDS open and ready for processing, the Do While loop proceeds to issue your macro command against each PDS member. Again, the program is using ISPF services to perform this work. This is the Address ispexec 'EDIT command.

After all PDS members have been processed, the last two lines of code free up the member list and close the dataset.


Conclusion

There's LOTS more edit macros can do. These examples only scratch the topmost surface of all the possibilities. To learn more, take a look at some of the useful sample edit macros in our Example Code section.


====> Like this article? Spread the link love to Slashdot, LXer, or wherever. Thanks..