KNOWLEDGE BASE
Log In    |    Knowledge Base    |    4D Home
Tech Tip: A 4D "Balancing" utility for finding paired closing character(s) in a text block
PRODUCT: 4D | VERSION: 14.1 | PLATFORM: Mac & Win
Published On: September 18, 2014

When working with text, most third party text, code, and method editors have a feature of function to aid a developer in highlighting the "Balancing" open and closing characters or tags. In 4D, it is a preference named "Highlight of the parentheses". In BBedit and Text Wrangler, its called "Balance". In Sublime Text, it has two names "Expand Selection to Brackets" and "Expand Selection to Scope."

The utility method, STR_FindBalancingChar, shown below the example images preforms such a function in 4D.

Example, with the block of JSON shown in Image 1, the need is to highlight for selection all the text contained between balanced "[{" and "}]".

Image 1




From the Edit menu, Image 2, the seleciton is made for this type of Balance selection.

Image 2




The resulting selection is show in Image 3 below.

Image 3




Utility Method STR_FindBalancingChar


If (True)
   If (False)
      Begin SQL
         /*
          Name: STR_FindBalancingChar ( $Text_P; $Open_T; $Close_T {; $Pos_L} )
          Path: STR_FindBalancingChar

          Purpose: Find the balancing end char(s) to the opening char()
          Example find balanced
          -- Parenthesis "(" to ")",
          -- Squar brackets "[" to "]",
          -- Curly brackets "{" to "}",
          -- Paired characters "[{" to "}]",
          -- or HTML/XML tags such as "<div>" to "</div>."

          $0 - C_LONGINT - Position of the closing
          $1 - C_POINTER - Pointer to the text to scan
          $2 - C_TEXT - Opening Char(s)
          $3 - C_TEXT - Closing Char(s)
          $4 - C_LONGINT - Offset into text starting point
         */
      End SQL
   End if
   C_TEXT($MethodName_T)
   $MethodName_T:=Current method name
    //===================== Declare Variables ==================================
    //method_parameters_declarations
   C_LONGINT($0)
   C_POINTER($Text_P;$1)
   C_TEXT($Open_T;$2)
   C_TEXT($Close_T;$3)
   C_LONGINT($Pos_L;$4)
    //--------------------------------------------------------------------------------
    //method_wide_constants_declarations
    //--------------------------------------------------------------------------------
    //local_variable_declarations
   C_LONGINT($Ndx;$SOA;$RIS;$Params_L;$Pos_L;$Opns_L;$Cls_L;$OPos_L;$CPos_L;$OLen_L;$CLen_L)
End if
//====================== Initialize and Setup ================================

$Pos_L:=0
$CPos_L:=0
$Opns_L:=0 // Count of opening brackets
$Cls_L:=0 // Count of closing brackets
$Params_L:=Count parameters
If ($Params_L>=3)
   $Text_P:=$1
   $Open_T:=$2
   $OLen_L:=Length($Open_T)
   $Close_T:=$3
   $CLen_L:=Length($Close_T)

   If ($Params_L=4)
      $Pos_L:=Position($Open_T;$Text_P->;$4)
   Else
      $Pos_L:=Position($Open_T;$Text_P->)
   End if
   If (Length($Text_P->)>=($Pos_L+$OLen_L+$CLen_L))
      $SOA:=$Pos_L

       //======================== Method Actions ==================================

      $Ndx:=0
      $OPos_L:=Position($Open_T;$Text_P->;$Pos_L)
      $Opns_L:=$Opns_L+1

      $CPos_L:=Position($Close_T;$Text_P->;$Pos_L+1)
      Repeat
         If ($OPos_L<$CPos_L)
            Repeat
               $Pos_L:=Position($Open_T;$Text_P->;$OPos_L+$OLen_L)
               If (($Pos_L>0) & ($Pos_L<$CPos_L))
                  $Opns_L:=$Opns_L+1
                  $OPos_L:=$Pos_L
               End if
            Until (($Pos_L>$CPos_L) | ($Pos_L<1))
         End if

         $Cls_L:=$Cls_L+1
         If ($Opns_L#$Cls_L)
            $CPos_L:=Position($Close_T;$Text_P->;$CPos_L+1)
            If ($Pos_L=0)
               $Cls_L:=$Cls_L+1
               $Pos_L:=$CPos_L
               While ($Pos_L>0)
                  $Pos_L:=Position($Close_T;$Text_P->;$CPos_L+1)
                  If ($Pos_L>0)
                     $Cls_L:=$Cls_L+1
                     $CPos_L:=$Pos_L
                  End if
               End while
            End if
         End if
      Until (($Opns_L=$Cls_L) | ($Pos_L<1))

       // Include the closing char(s)
       //
      $CPos_L:=$CPos_L+$CLen_L

       //======================== Clean up and Exit =================================

      If (Not(Asserted($Opns_L=$Cls_L;$Open_T+" and "+$Close_T+" are unbalanced from starting point "+String($SOA)+".")))
         $CPos_L:=0
      End if
   End if
End if

$0:=$CPos_L


The method was called using the code below...

C_TEXT($Open_T;$1)
C_LONGINT($Ndx;$SOA;$RIS;$Params_L;$Start_L;$End_L)
C_TEXT($End_T)

//====================== Initialize and Setup ================================

$Params_L:=Count parameters
$Open_T:=Get selected menu item parameter
Case of
   : ($Open_T="[")
   $End_T:="]"

   : ($Open_T="{")
   $End_T:="}"

   : ($Open_T="[{")
   $End_T:="}]"

Else
   ASSERT(False;"Unknown case test!")

End case
//======================== Method Actions ==================================

GET HIGHLIGHT(*;"MyEdit_T";$Start_L;$End_L)
$Ndx:=Position($Open_T;MyEdit_T)
While (($Ndx>0) & ($Ndx<=$Start_L))
   $SOA:=$Ndx
   $Ndx:=Position($Open_T;MyEdit_T;$SOA+1)
End while
$Start_L:=$SOA
$End_L:=STR_FindBalancingChar (->MyEdit_T;$Open_T;$End_T;$Start_L)
HIGHLIGHT TEXT(*;"MyEdit_T";$Start_L;$End_L)