PerfectScript Journal

February 1999: Making Macros Easy to Maintain

I often receive requests from clients to convert an older macro to WPWin 7 or 8.
Invariably, I find the macro so poorly coded and documented that it is much easier
to just rewrite it from scratch rather than to try to fix it or convert it. In this month's
journal, I'll talk about techniques you can use to make your macros more readable,
easier to maintain, and easier to convert once you take the plunge to a new version
of WordPerfect.

Use Variables for Paths and Filename

When the macro will be accessing specific files or directories a lot, create variables
for the names of those files and directories. You can then use the variable names
throughout the macro as required.

I recommend making a PROCEDURE that all the path and filename declarations will
go into. You can even call this PROCEDURE from other macros to set the values of
the directories. If your paths or filenames ever change (and they eventually will),
you need only change one section in the macro to update all the routines, or even
other macros.

You can use this technique for any data that will be needed throughout the macro, or
that should be accessible for any reason in case a change is required later. Other
examples of data that should be placed in variables are people's name, company
names, street addresses, phone numbers, and so on.

Variables are local to the routine where they are created so you'll need to either make
the variables Global, or assign the variables by reference so that the calling macro
section can see them.

Here's an example of using a PROCEDURE and Global variables:


Application(A1; "WordPerfect"; default; "EN")
// main
SetFilePaths()
Type(vMainDocPath)
HardReturn
// end main

//**********************************************************************
//*	Routine Name:	    SetFilePaths
//*	Input Variables:	none
//*	Return Variable:	None
//*	Description:	    Sets global var for paths/filenames to be used in the macro
//**********************************************************************
PROCEDURE SetFilePaths()
GLOBAL (vMainDocPath; vClient1Path; vClient2Path;vClientIndexFile)
vMainDocPath:="c:\workfiles\"
vClient1Path:=vMainDocPath+"Client1\"
vClient2Path:=vMainDocPath+"Client2\"
vClientIndexFile:=vmainDocPath+"index\clientindex.wpd")
ENDPROC
          

If you prefer not to use global variables (and I recommend avoiding Globals when
possible), you can assign the path and filenames by reference. This means that you
actually get a local variable with the values that were assigned in the PROCEDURE. To
do this, you include the variable names as parameters in the SetFilePaths() PROCEDURE,
and include an ampersand (&) in front of the variable name.

Remember that variables are normally local to the routine they are assigned in. When
you pass the variable names by reference, you are telling PerfectScript to transfer the
values assigned in the SetFilePaths() routine to variables in the calling level of the macro
also.

Generally, it is a good idea to assign some value (even if just a "") to the variables first,
so that they already exist in memory before calling the SetFilePaths() routine, as shown below.


Application(A1; "WordPerfect"; default; "EN")
vMainDocPath:=""               // set the values to "" first
vClient1Path:=""
vClient2Path:=""
vClientIndexFile:=""
// pass the variable names in by reference
SetFilePaths(&vMainDocPath; &vClient1Path; &vClient2Path; &vClientIndexFile)
// the variables now exist at this level of the macro

//**********************************************************************
//*	Routine Name:	    SetFilePaths
//*	Input Variables:	Variable names to pass the values back in by reference
//*	Return Variable:	None
//*	Description:	    Sets global var for paths/filenames to be used in the macro
//**********************************************************************
PROCEDURE SetFilePaths(&vMainDocPath; &vClient1Path; &vClient2Path; &vClientIndexFile)
vMainDocPath:="c:\workfiles\"
vClient1Path:=vMainDocPath+"Client1\"
vClient2Path:=vMainDocPath+"Client2\"
vClientIndexFile:=vmainDocPath+"index\clientindex.wpd")
ENDPROC
          

Break tasks down into their smallest logical duties

You should always write your code as a series of small tasks. As you plan your
macro before starting to code, this will all fall into place. For example,

The macro will open the file
Make a backup copy of the file
Add some text to the file
Sort the file
Save the file
Close the file

This high level plan can become the basis of breaking your code into smaller tasks.
In this case you could have a routine for opening the file, one for making the backup,
and so on. When you want to macro to perform a certain task, you just call the routine
that was designed to do that task.

If a task will vary periodically (for example, the filename or the sort keys may change),
you can pass parameters to the PROCEDURE to customize each instance when you use
that task.

In the following macro, there are three separate tasks: Set Paths, Open File, and Sort file.
Each task is located in a PROCEDURE. At the top of the macro, I assign the desired
variables, then call the routine to set values to those variables.

I then call the routine that opens a document, then the routine that sorts the document.
All the basic steps are laid out in the main macro code, with the work actually being done
by subroutines. If I want to troubleshoot the sorting routine, I can just look at the SortDoc
procedure to see what the macro is doing.


Application(A1; "WordPerfect"; default; "EN")
vMainDocPath:=""               // set the values to "" first
vClient1Path:=""
vClient2Path:=""
vClientIndexFile:=""

// main 
SetFilePaths(&vMainDocPath; &vClient1Path; &vClient2Path; &vClientIndexFile)
OpenDoc(vClientIndexFile; TRUE)
SortDoc(-1; 1; 2; SortKeys.SortOrder.Ascending!)

//**********************************************************************
//*	Routine Name:	    SetFilePaths
//*	Input Variables:	Variable names to pass the values back in by reference
//*	Return Variable:	None
//*	Description:	    Sets global var for paths/filenames to be used in the macro
//**********************************************************************
PROCEDURE SetFilePaths(&vMainDocPath; &vClient1Path; &vClient2Path; &vClientIndexFile)
vMainDocPath:="c:\workfiles\"
vClient1Path:=vMainDocPath+"Client1\"
vClient2Path:=vMainDocPath+"Client2\"
vClientIndexFile:=vmainDocPath+"index\clientindex.wpd"
ENDPROC

//**********************************************************************
//*	Routine Name:	    SortDoc
//*	Input Variables:	Variable names to pass the values back in by reference
//*	Return Variable:	None
//*	Description:	    Sets global var for paths/filenames to be used in the macro
//**********************************************************************
PROCEDURE SortDoc (vFirstKey; vSecondKey; vThirdKey; vOrder)
SortKeys (
{Field: 1; Line: 1; Word: vFirstKey; SortType: Alphanumeric!; SortOrder: vOrder;
Field: 1; Line: 1; Word: vSecondKey; SortType: Alphanumeric!; SortOrder: vOrder;
Field: 1; Line: 1; Word: vThirdKey; SortType: Alphanumeric!; SortOrder: vOrder})
SortType (SortType: LineSort!)
SortAction (SortAction: Sort!)
SortUndo (State: NoUndo!)
SortCaseOrder (Case: LowercaseFirst!)
Sort ()
ENDPROC

//**********************************************************************
//*	Routine Name:	OpenDoc
//*	Input Variables:	vFilename: The name of the file to open
//*				vInsert: TRUE = insert into current document
//*					FALSE = open in new screen
//*	Return Variable:	None
//*	Description:	    Opens the specified document
//**********************************************************************
PROCEDURE OpenDoc(vFilename; vInsert)
If(vInsert = TRUE)
	FileInsert(vFilename; ; Insert!)
Else
	FileOpen(vFilename)
Endif
ENDPROC

          

Comment Profusely

The best advise in making macros easy to maintain is to comment everything profusely.
As you can see in the examples above, I use a standard header for my PROCEDUREs.
In that header I include the Routine name, a description of any input parameters,
the return value (this is applicable to FUNCTIONS), and a brief description of the
purpose of the procedure.

If you have to use confusing code, or do something out of the ordinary, you will
thank yourself later for adding comments explaining why you coded the way you did.

//**********************************************************************
//* Routine Name:    name of the routine here
//* Input Variables: name and description of paramters n
//* 		vExample:  TRUE = pass in a true 
//* 			   FALSE = pass in a false
//* Return Variable: What return value is passed back (for FUNCTIONS)
//* Description:     A description of what the routine accomplishes
//**********************************************************************          

For more information on dialogs and DLL calls, get a copy of my book.

For information on ordering my book see my web page.

Copyright Notice: The information included in the PerfectScript Journal is
protected by US Copyright. The author grants you the right to use the routines
in your own macros as needed. You may not sell, distribute, or publish them in
any form.
If you choose to use the information here, you do so entirely at your own risk.
No representations are made regarding the fitness of this information for your
particular purpose, or for your ability or inability to use the information. You
are advised to make backups of all relevant files before implementing any suggestion
or technique.
© Copyright 1999 by J. Jeppson.