Highlighting Stuff in Text

In general, the text formatting capabilities are a bit limited in Appian. I consider this to be a good thing, as limiting choice to the right options makes development a lot easier. And we are talking about low-code, so this is part of the deal.

Now, I had a requirement to highlight specific words in user entered text. Think of a @mention or a URL that should be clickable. So, I put some regex magic, text parsing and my usual set of Appian tricks into a cauldron, started stirring and hoped for the best.

Finding Patterns in Text

To find things in text, regex is the Swiss army knife to use. In Appian, I deployed the regex plugin to get started. The function regexsearch allows me to search for specific patterns. The important part here is the output of this function. I get a list of a data structure which includes the matching text itself, but also the positions at which that match starts and ends in the original text. Using this information, I can easily implement a dynamic replacement logic.

The basic idea is to take the text up to the start position of the match, replace the match by some modified version., then take the text from the end position. Then repeat this for each match. I pass the match to a separate expression to make it more dynamic.

formatTextMatches(text(Text), regexPattern(Text), component(Any))

a!localVariables(
  local!urls: if(
    a!isNullOrEmpty(ri!component),
    error("ri!component is missing its reference!"),
    if(
      a!isNullOrEmpty(ri!text),
      null,
      regexsearch(
        ri!regexPattern,
        ri!text,
        "gms"
      )
    )
  ),
  if(
    a!isNullOrEmpty(local!urls),
    ri!text,
    /* Iterate on list of matches */
    a!forEach(
      items: local!urls,
      expression: {
        if(
          fv!isFirst,
          /* The first text snippet */
          mid(ri!text, 1, fv!item.startPosition),
          ""
        ),
        /* Call the passed expression to replace the match with a rich text item */
        ri!component(fv!item.match),
        if(
          fv!isLast,
          /* Add the remaining text */
          mid(ri!text, tointeger(fv!item.endPosition) + 1, 999999),

          /* Add text up to the next match */
          mid(
            ri!text,
            tointeger(fv!item.endPosition) + 1,
            tointeger(local!urls[fv!index + 1].startPosition) - tointeger(fv!item.endPosition)
          )
        )
      }
    )
  )
)

The Replacement Function

You probably identified the rule input component. It is made to make this a bit more versatile. The idea is, to provide some text, a regex pattern and an expression that takes the match and returns a replacement in the form of a rich text item. Passing function references is key to create any kind of customisable components in Appian.

formatUrlMatches(url(Text))

if(
  a!isNullOrEmpty(ri!url),
  ri!url,
  {
    a!richTextItem(
      text: ri!url,
      link: a!safeLink(
        uri: ri!url,
        openLinkIn: "NEW_TAB"
      )
    ),
    " ",
    a!richTextIcon(
      icon: "external-link",
      link: a!safeLink(
        uri: ri!url
      ),
      caption: "Open link in new tab",
      altText: "Open link in new tab",
    )
  }
)

I put this into a little interface for testing. The outcome is spectacular.

Summary

I got a great magic potion out of my cauldron while learning even more about regex and text parsing. I am curious about what you will make out of this. Looking forward to your feedback.

Keep up the happy low-code magic in Appian!

Leave a Reply