StylePatch


StylePatch


Methods

Of (Class method)

of

Creates a new StylePatch object containing the provided styles. This is the only way to create a Patch object (the new class method is a private method).

Styles is an ordered set of lines. These lines may be separated by semicolons, or be items in a Rexx Array, or both.

Style patches

-- Sample style patch file
-- =======================
--
-- Lines starting with "--" are comments and are ignored
--
-- Patch simple variable elements to display as bold black over 75% yellow
element EL.SIMPLE_VARIABLE #000/#cc0 bold
-- Patch method names to display as black over 75% magenta
name  METHOD.NAME        #000/#c0c

Style patches have a very simple format: they are arrays of strings consisting of:

  • Blank lines, containing only whitespace, which are completely ignored.
  • Comments, starting with two dashes "--", which also are completely ignored.
  • Other lines containing semicolons. In that case, these lines are split at the semicolons, the semicolons are discarded, and the lines are inserted at the beginning of the list.
  • Once comment lines starting with "--" have been discarded, all dashes "-" are replaced by blanks " ". This can be very useful in certain contexts like certain Linux shells, where enclosing arguments between quotes may be cumbersome.
  • Highlighting patches for element categories:
    Element class highlighting
    Element can be abbreviated to E, and the class name can omit the EL. prefix, if desired.
  • Highlighting patches for element category sets:
    All set highlighting
    All can be abbreviated to A, and the set can omit the ALL. prefix, if desired.
  • Highlighting patched for taken constant names:
    Name constantName highlighting
    Name can be abbreviated to N, and the constantName can omit the .NAME suffix, if desired.
  • Highlighting is a blank-separated sequence of case-insensitive elements, selected between
    • Foreground colors, in the format #rgb, #rrggbb, or one of the 147 standard CSS named colors defined here.
    • Foreground/background color combination, in the format fg/bg (with no blanks), where fg and bg are either #rgb, #rrggbb, or one of the 147 CSS named colors defined here.
    • One of the single words bold, italic or underline.
    • The single word no, which has to be followed by one of bold, italic or underline.
  • Element categories, category sets, and subcategories are described in detail here.
::Method methodName
  -- In this code fragment, the standard dark mode highlighting style is used.
  -- Additionally, local variables are specially highlighted with a bold black
  -- font over a yellow background, and the highlighting for method names is
  -- modified to have a black foreground over a magenta background.
  len = Length("String")
  n   = Pos("x", value)

Program source

/******************************************************************************/
/*                                                                            */
/* StylePatch.cls -- Highlighting style patches                               */
/* ============================================                               */
/*                                                                            */
/* This program is part of the Rexx Parser package                            */
/* [See https://rexx.epbcn.com/rexx.parser/]                                  */
/*                                                                            */
/* Copyright (c) 2024-2025 Josep Maria Blasco <josep.maria.blasco@epbcn.com>  */
/*                                                                            */
/* License: Apache License 2.0 (https://www.apache.org/licenses/LICENSE-2.0)  */
/*                                                                            */
/* Version history:                                                           */
/*                                                                            */
/* Date     Version Details                                                   */
/* -------- ------- --------------------------------------------------------- */
/* 20241206    0.1  First public release                                      */
/*                                                                            */
/******************************************************************************/

env = .environment
If env~StylePatch == .Nil Then env~StylePatch =  .StylePatch

--------------------------------------------------------------------------------

::Class StylePatch Public

-- Make the NEW method private

::Method new Class Private
  Use Strict Arg styles
  Return self~new:super(styles)

-- Instances should be created by calling the OF class method

::Method Of Class

  Use Strict Arg styles

  If styles~isA(.String) Then styles = .Array~of( styles )
  Else .Validate~classType( "styles",   styles,    .Array )

  clean = .Array~new
  Loop style Over styles
    style = Strip(style)
    If style[1,2] == "--" Then Iterate
    Loop line Over style~makeArray(";")
      line = Strip(line)
      If line[1,2] == "--" Then Iterate
      If line      ==   "" Then Iterate
      clean~append(line~changeStr("-"," "))
    End
  End

  patch = self~new( clean )

  Loop line Over clean
    Parse Lower Var line mode rest
    Select
      When Abbrev("all",    mode) Then Call ParseAll
      When Abbrev("element",mode) Then Call ParseElement
      When Abbrev("name",   mode) Then Call ParseName
      Otherwise Signal BadMode
    End
  End

  Return patch

ParseElement:
  Parse Var rest category highlight
  ucategory = Upper(category)
  If \ucategory~startsWith("EL."), \ucategory~startsWith("ALL.") Then
    ucategory = "EL."ucategory
  classes = .environment[ucategory]
  If classes == .Nil Then Signal BadClass
  patch~add(classes, highlight)
Return

ParseAll:
  Parse Var rest category highlight
  ucategory = Upper(category)
  If \ucategory~startsWith("ALL.") Then
    ucategory = "ALL."ucategory
  classes = .environment[ucategory]
  If classes == .Nil Then Signal BadClassSet
  patch~add(classes, highlight)
Return

ParseName:
  Parse Var rest name highlight
  uName = Upper(name)
  If \uName~endsWith(".NAME"), \uName~endsWith(".VALUE") Then
    uName = uName".NAME"
  classes = .environment[uName]
  If classes == .Nil Then Signal BadName
  patch~addName(classes, highlight)
Return

BadName:
  Raise Syntax 88.900 Additional("Invalid name '"name"'")

BadClass:
  Raise Syntax 88.900 Additional("Invalid element category '"category"'")

BadClassSet:
  Raise Syntax 88.900 Additional("Invalid element category set '"category"'")

BadMode:
  Raise Syntax 88.900 Additional("Invalid option '"line"'")

--------------------------------------------------------------------------------

::Method Init
  Expose patch. Tag2Attr. source
  Use Strict Arg source
  patch.    = .Stem~new
  Tag2Attr. = ""
  patch.!Tag2Attr = Tag2Attr.
  self~init:super

::Method getpatch
  Expose patch.
  Return patch.

::Method AddName
  Expose Tag2Attr.
  Use Strict Arg name, attributes
  constant = .EL.TAKEN_CONSTANT
  attr = self~parse( attributes )
  Tag2Attr.constant.name = attr

::Method Add
  Expose Tag2Attr.
  Use Strict Arg classes, attributes
  attr = self~parse( attributes )
  Do c Over classes~makeArray("")
    Tag2Attr.c = attr
  End

::Method parse Private
  Use Arg attributes
  option  = ''
  If attributes = "" Then Signal BadOption
  attr    = "."
  fg      = ""
  bg      = ""
  no      = 0
  Do option Over attributes~space~makeArray(" ")
    option = Lower( option )
    If option == "no" Then Do
      no = 1
      Iterate
    End
    Select Case option
      When "bold"      Then
        If no Then attr ||= "b"
        Else       attr ||= "B"
      When "italic"    Then
        If no Then attr ||= "i"
        Else       attr ||= "I"
      When "underline" Then
        If no Then attr ||= "u"
        Else       attr ||= "U"
      Otherwise
        If no Then Signal BadNo
        color = option
        If color~contains("/") Then Do
          Parse Var color color"/"background
          If background[1] \== "#" Then Do
            bg = .HTML.Color[background]
            If bg == .Nil Then Signal BadBackground
          End
          Else Do
            bg = SubStr(background,2)
            If \"3 6"~containsWord( Length(bg) ) Then Signal BadBackground
            If \bg~dataType("X") Then Signal BadBackground
            bg = Normalize(bg)
          End
        End
        If color[1] \== "#" Then Do
          fg = .HTML.Color[color]
          If fg == .Nil Then Signal BadColor
        End
        Else Do
          fg = SubStr(color,2)
          If \"3 6"~containsWord( Length(fg) ) Then Signal BadColor
          If \fg~dataType("X") Then Signal BadColor
          fg = Normalize(fg)
        End
    End
    no = 0
  End
  If no Then Signal BadFinalNo
  Return attr fg"/"bg

Normalize:
  If Length(Arg(1)) == 6 Then Return Arg(1)
  Parse Value Arg(1) With a +1 b +1 c +1
  Return Space( a a b b c c , 0 )

BadNo:
  Raise Syntax 93.900 Additional("Invalid option 'no "option"'")

BadFinalNo:
  Raise Syntax 93.900 Additional("Invalid option 'no'")

BadOption:
  Raise Syntax 93.900 Additional("Invalid option '"option"'")

BadColor:
  Raise Syntax 93.900 Additional("Invalid color '"color"'")

BadBackground:
  Raise Syntax 93.900 Additional("Invalid background color '"background"'")