code: public List<MarkupText.SubText> findTokens(Pattern pattern) {
          String text = getText();
          Matcher m = pattern.matcher(text);
          List<SubText> r = new ArrayList<>();
  
          while(m.find()) {
              int idx = m.start();
              if(idx>0) {
                  char ch = text.charAt(idx-1);
                  if(Character.isLetter(ch) || Character.isDigit(ch))
                      continue;   // not at a word boundary
              }
              idx = m.end();
              if(idx<text.length()) {
                  char ch = text.charAt(idx);
                  if(Character.isLetter(ch) || Character.isDigit(ch))
                      continue;   // not at a word boundary
              }
              r.add(createSubText(m));
          }
  
          return r;
      }
docs: Find all "tokens" that match the given pattern in this text.
  
  <p>
  A token is like a substring, except that it's aware of word boundaries.
  For example, while "bc" is a string of "abc", calling {@code findTokens}
  with "bc" as a pattern on string "abc" won't match anything.
  
  <p>
  This method is convenient for finding keywords that follow a certain syntax
  from natural text. You can then use {@link MarkupText.SubText#surroundWith(String,String)}
  to put mark up around such text.