There are many common coding requirements which will be needed by many macros. Below are small common coding techniques showing how these various requirements can be handled. These are not full, complete macros but only small code blocks showing one particular aspect.


Fragments include:


Accessing macro operands as Line Range operands

Accessing all macro operands as a list

Modifying data in a text line

Setting a final Return Code and Message from a Macro

Setting a new cursor location to be used when the macro ends

Perform a simple action on all lines in a file

Use the FIND command to locate text lines to process

Locate text lines to process using non-FIND criteria

Insert a New Text line into the File

Search for and process all lines with a specific Tag ID

Locate 'Problem' lines and add a NOTE line to mark them

Using Global storage to communicate between Macros

Create a new Line Command



Accessing macro operands as Line Range operands


Assume a macro which accepts two operands, a starting and an ending line range.


dim fromlptr, tolptr as number

fromlptr = Get_LPtr(Get_Arg$(1))            ' Fetch from operand

tolptr = Get_LPtr(Get_Arg$(2))              ' Fetch to operand

if fromlptr = 0 or _                        ' Validate what we got

  tolptr   = 0 or _                        '

  fromlptr > tolptr then                   '

  Halt(FAIL, "From / To line pointers missing or invalid")

end if


Note:  The use of Get_LPtr  to convert the arguments to internal line pointer format. After this code the fromlptr and tolptr variables are all ready to be used in further processing code. The if statement uses line continuation characters  _ to format the statement as one test per line. (Without the underscore, each thinBasic statement ends on the line it starts on. This is a standard BASIC coding convention.)


Macro support also allows a more structured form which automatically fetches command operands. Here's the above sample done in that structured format:


' MyMacro.MACRO

SUB MainLine(FromArg as string, ToArg as string)

dim fromlptr, tolptr as number

fromlptr = Get_LPtr(FromArg)                ' Convert the from operand

tolptr = Get_LPtr(ToArg)                    ' Convert the to operand

if fromlptr = 0 or _                        ' Validate what we got

  tolptr   = 0 or _                        '

  fromlptr > tolptr then                   '

  Halt(FAIL, "From / To line pointers missing or invalid")

end if                                      '

END SUB                                     ' End of MainLine SUB


And a third method is also available, particularly useful with macros that may have many operands available. See Full Parse Access for details.



Accessing all macro operands as a list


Assume a macro which accepts any number of operands, and processes each one at a time.


dim OpIndex as number

dim Operand as string


if Get_Arg_Count = 0 then halt(FAIL, "No operands are present")


for OpIndex = 1 to Get_Arg_Count            ' Loop through operands

  Operand = Get_Arg$(OpIndex)              ' Fetch the operand

  ... process each Operand value ... ' Process it

next


Note:  This uses a simple FOR / NEXT loop to fetch and process each operand.



Modifying data in a text line


Assuming the macro has a valid line pointer to a specific data line, there are a variety of methods to change the text.


Replace the entire line


This is done with the Set_Line function.


     Set_Line(line-ptr, replacement-text)


Which will completely replace any existing text in the line with the replacement-text value



Replace a substring of the text


This is done with the  SPF_REP function


     SPF_REP(line-ptr, column, replacement-text)


Which will completely replace a portion of the existing text, starting at column with the replacement-text. If the existing text is less than column characters long, it will be first extended with spaces.



Insert a text string into the existing text


This is done with the  SPF_INS function


     SPF_INS(line-ptr, column, insert-text)


Which will insert a string (insert-text) into existing text, following the  column position. If the existing text is less than column characters long, it will be first extended with spaces.



Overlay a text string into the existing text


This is done with the  SPF_OVR function


     SPF_OVR(line-ptr, column, overlay-text)


The SPF_OVR function will overlay a character string within a data line starting at a specified column. Overlay is done by comparing relative characters within the overlay area and then copying characters from the provided string to the data line only if the same relative character in the data line is a blank. This is identical in function to the SPFLite line commands CC / OO.



Overlay Replace a text string into the existing text


This is done with the  SPF_OVR_REP function


     SPF_OVR_REP(line-ptr, column, overlay-text)


The SPF_OVR_REP function will overlay a character string within a data line starting at a specified column. Overlay replace is similar to Overlay (above) except that Overlay Replace gives priority to the new character string. The Overlay is done by comparing relative characters within the overlay area and then copying characters from the provided new string which are non-blank to the data line regardless of the contents of the data line. Blanks in the new character string are not copied, thus leaving the original data line characters untouched. This is identical in function to the SPFLite line commands CC / ORR.



Setting a final Return Code and Message from a Macro


When a macro completes, it is often useful to have SPFLite issue a status message to indicate the relative success or failure of the macro processing. For some macros, this message might be the only desired output (for example, from a word-counting macro). This can be done by either of two very similar functions. Set_Msg and Halt The difference is that Set_Msg establishes the RC and message values and macro processing continues. Halt establishes the values and terminates the macro immediately.


Set_Msg("This macro ran successfully")


Set_Msg(0, "The number of words in the file is: " + TSTR$(wordctr))


Halt(4, "There were no strings found")


Halt(FAIL, "A required operand is missing")


The first operand of Set_Msg/Halt  is optional, but if provided, and the value is numeric, it is assumed to be the desired return code for the macro, and the second is the message string.


If the first operand is a string operand, it is assumed to be the message text, and a default Return Code value of 0 (zero) is assumed.


Note the first example above uses the default RC format, the other three examples provide specific RC values.


A return code of 0 means the macro was successful; 4 means you wish to issue a warning, and 8 means that the macro has "failed", based on your definition of what you mean for the macro to fail.


Rather than hard-coding these values, you can specify them symbolically, using OK for 0, WARN for 4 and FAIL for 8.


For non-Zero RC values, SPFLite will format the value and  prefix the message text with RC=nn:    e.g. RC=8: Missing search string


For compatibility with future releases of SPFLite, the return code operands you use should only contain 0, 4 or 8, or the symbolic names for them.



Setting a new cursor location to be used when the macro ends


You may optionally set a new cursor location to be used on macro exit. If you do not, the cursor will remain where it was when the macro started or where some other SPFLite command positioned it, if SPFLite command(s) were invoked by the macro.


The Set_Csr function provides the line pointer, the column number, and an optional length value.


If the column number provided is zero, the cursor will be placed in the line number area of the line.


If the optional length parameter is zero, no text will be highlighted.


If the optional length parameter is provided, then the text at the cursor location, for the specified length, will be highlighted.



Set_Csr(line-ptr, column, 0)    ' Cursor to line-ptr, column, with no highlighting


Set_Csr(line-ptr, 0)            ' Cursor to the line number area of line-ptr


Set_Csr(line-ptr, column, 5)    ' Cursor to line-ptr, column, with a 5 chars highlighted



Perform a simple action on all lines in a file


Assume a portion of macro which wants to perform an action on all lines in the file.


dim i as number


for i = Get_First_Lptr to Get_Last_Lptr       ' Loop through the file

  if Is_Data(i) then                         ' Doing only data lines

     Set_Line(i, ucase$(Get_Line$(i)))       ' SAMPLE ACTION: uppercase the text

  end if                                     '

next                                          '


Note:  This uses a simple FOR / NEXT loop to process each line. An Is_Data test ensures we only process text data lines rather than on special lines. The action performed here is just a simple uppercase function on each line's data.


An alternate method using the Get_Next_LPtr function could be:


dim i as number value 1


i = Get_Next_LPtr(i, 1, "DATA")            ' Adjust i to the next DATA line

do while istrue i                          ' If not end of file

  Set_Line(i, ucase$(Get_Line$(i)))       ' SAMPLE ACTION: uppercase the text

  i = Get_Next_LPtr(i, 1, "DATA")         ' Adjust i to the next DATA line

loop                                       ' loop-de-loop


Starting in SPFLite version 8.4, you can write code to access all lines in a file using a simplified syntax. The WHEN option allows the check for the correct type of line, right on the FOR statement. It operates just like the code above, without requiring to have an IF statement nested inside the FOR:


dim i as number


for i = Get_First_Lptr to Get_Last_Lptr WHEN Is_Data(i)  

  Set_Line(i, ucase$(Get_Line$(i)))       ' SAMPLE ACTION: uppercase the text

next


Note:  Thanks to Eros Olmi of thinBasic for adding this support to his Basic engine for us.


Use the FIND command to locate text lines to process


To locate specific lines to process using the FIND command use the following code. Assume the string to search for is already set up in the variable lookfor.


SPF_Cmd("FIND FIRST " & lookfor)                      ' Issue 1st FIND cmd

do while Get_RC = 0                                   ' while found

  IF other tests needed to 'qualify' this record for processing

  ... process the found record - - -                 ' Process it

      ... The line's text can be obtained with

      ... Get_Line$(Get_Find_LPtr))

      ... The found string will be at the column provided by

      ... Get_Find_Col

  end if                                             '

  SPF_Cmd("RFIND")                                   ' Look some more

loop                                                  '


Note:  The ...process the found record ... code can use the data from Get_Find_LPtr,  Get_Find_Col and Get_Find_Len functions since the code is only executed following a successful FIND command ( Get_RC = 0 ).



Locate text lines to process using non-FIND criteria


To locate specific lines to process using some other criteria than what the FIND command can provide, use the following code.


dim i as number

for i = 1 to Get_Last_LPtr                            ' Let's search

  if is_Data(i) then                                 ' Only data lines

     if ... special criteria test ... then           ' Examine the line

        ... process the line - - -                   '

     end if                                          '

  end if                                             '

next i                                                '



Note:  The ...process the found record ... code can use the data from Get_Find_LPtr,  Get_Find_Col and Get_Find_Len functions since the code is only executed following a successful FIND command ( Get_RC = 0 ).



Insert a New Text line into the File


This sample assumes the macro has already determined the line-pointer after which a line is to be inserted. We will assume this is in the variable curr_Lptr.


       SPF_CMD("LINE N1 !" & TSTR$(curr_Lptr)) ' Insert a line after curr one


       Set_Line(curr_Lptr + 1, "New text for the new line")


       INCR curr_Lptr                          ' Adjust curr_Lptr for the inserted line



Note:  The new text line is inserted using the primary command LINE. The Set_Line function adds the new text, and uses Curr_Lptr + 1 for the line number, since that will be the new line pointer for the inserted line. The third line, which increments the Curr_Lptr value, is what would probably be needed in most macros to adjust the line pointer value for the inserted line. That's because Curr_Lptr points to the "current" line, and the line inserted after the current line would have a line pointer one greater than the current one. Whether this would actually be needed is of course dependent on the design of the logic flow in the specific macro.


If you wish to use the Line Number value directly (rather than a pointer), the first line of this example can be specified using the . (period) notation like this:


       SPF_CMD("LINE N1 ." & TSTR$(Get_LNUM(curr_Lptr)))  



Search for and process all lines with a specific Tag ID


This sample locates all lines with a Tag of :ABC and processes them.


dim i as number value 1

dim tagname as string value ":ABC"


i = Get_Next_LPtr(i, 1, tagname)           ' Adjust i to the next :ABC tagged line

do while istrue i                          ' If not end of file

  Set_Line(i, ucase$(Get_Line$(i))        ' SAMPLE ACTION: uppercase the text

  i = Get_Next_LPtr(i, 1, tagname)        ' Adjust i to the next DATA line

loop                                       ' loop-de-loop



Locate 'Problem' lines and add a NOTE line to mark them


This sample will locate lines which match some unique criteria, and insert a NOTE line following the line to flag the problems.


dim cLPtr as number value 1



while cLPtr < Get_Last_LPtr                           ' Loop through all lines

  if Is_Data(cLPtr) then                             ' Only do data lines

     if ...special criteria one ... then             ' A problem line?

        SPF_CMD("LINE NOTE !" + TSTR$(cLPtr))        ' Insert a NOTE line after this one

        Set_Line(cLPtr + 1, "Check this line - special criteria one")

        incr cLPtr                                   ' Adjust cLPtr for the inserted line

     elseif ...special criteria two ... then         ' A different problem line?

        SPF_CMD("LINE NOTE !" + TSTR$(cLPtr))        ' Insert a NOTE line after this one

        Set_Line(cLPtr + 1, "Check this line - special criteria two")

        incr cLPtr                                   ' Adjust cLPtr for the inserted line

     end if                                          ' End of special tests

  end if                                             ' End of Is_Data tests

  incr cLPtr                                         ' Bump to next line

wend

     


Using Global storage to communicate between Macros


In this sample, Macro A saves the location of the cursor when it is invoked in Global storage. Later, another macro Macro B wishes to return the cursor to the original location stored by Macro A.


Macro A


'--- Save where we are for Macro B, use key names MacALine and MacACol for global storage pool

Set_Gbl_Num("MacALine", Get_LNum(Get_Csr_LPtr))      ' Save where the cursor is

Set_Gbl_Num("MacACol", Get_Csr_Col)                  '


Macro B


dim i, j as number            


i = Get_Gbl_Num("MacALine")                           ' Get saved line number

j = Get_Gbl_Num("MacACol")                            ' Get saved column

if i = 0 then halt(FAIL, "No location saved by Macro A")

Set_Csr(Get_LPtr(i), j, 0)                            ' Put cursor back where it was



Create a new Line Command


To create a new line command, do the following:

    • Choose your command name. It must be short and cannot conflict with any existing line or primary commands.
    • Line-command macro names by nature must be short; otherwise they cannot be entered in the sequence area of the edit screen.
    • Line-command macros cannot contain digits in the name.
    • It is possible to have a line-command macro with a single-letter name. Bear in mind that most of the single letter commands are already taken by SPFLite. The letters remaining available for use as single-letter macro names are  E  K  P  Q  U  V   Y  and  Z. The macro files such macros are stored in would use the normal naming conventions. So, an E line-command macro would be stored in the file E.MACRO.
    • For our example, we'll use CT (for Center Text). SPFLite will also automatically recognize CTT as the block form of this command, and will process any modifiers that imply a block, such as n or a / or \ modifier.
    • You can also force a line macro of any format to be a Block or single line macro; see "Macro Format and Structure" for details.
    • Create the macro name as the "singular form" of CT.MACRO (not CTT.MACRO)
    • There is no other setup required to activate this as a line command. The presence of CT.MACRO in the \MACROS folder is sufficient.



Example:


' CT.MACRO

dim tt, tt2 as string  

dim lno1, lno2, width, i as number

  ' Ensure we're called correctly

  if Is_Line_Cmd = FALSE then Halt(fail, "CT/CTT macro was not issued as a line command")


  '----- Get the line number range  

  lno1 = Get_Src1_Lptr                               ' From line

  lno2 = Get_Src2_Lptr                               ' To line

  width = Get_Line_Op                                ' Get the centering width

  if width = 0 then width = Get_RBound               '


  for i = lno1 to lno2                               ' Loop through the line range

     if Is_Data(i) then                              ' Just Data lines

        tt = trim$(Get_Line$(i))                     ' Get the trimmed text

        if width = 0 then width = Get_Line_Len(i)    ' Use line length if no other.

        if len(tt) > width then                      ' Center possible?

           Halt(warn, "One or more lines exceed Center length")

        else                                         '

           tt2 =  repeat$((width - len(tt)) / 2, " ") + tt ' Center it

           Set_Line(i, tt2)                          ' Stuff it back

        end if                                       '

     end if                                          '

  next                                               '

  halt                                               ' Done


Note:  The check of Is_Line_Cmd to ensure the macro is invoked as a line command. The line range to be processed is obtained from Get_Src1_LPtr and Get_Src2_LPtr. For a single line selection, these two values would be identical. A FOR / NEXT loop is used to process each line. An Is_Data test ensures we only process text data lines and not special lines.


Created with the Personal Edition of HelpNDoc: Full-featured multi-format Help generator