0

I have a Google Doc (only the type like Word, not spreadsheets or anything else) where I need to iterate over a series of rules (regular expressions) and style whatever matches the rules via an add on. The main body looks like this:

function showIssues() {
  var body = DocumentApp
             .getActiveDocument()
             .getBody();
  
  const rules = {
    fluff: {
      match: '\b(just|very)\b',
      color: '#FFDDDD'
    },
    adverbs: {
      match: '(\w+ly)\b',
      color: '#FF00FF'
    }
  };

  for (const [rule, definition] of Object.entries(rules)) {
    body.replaceText(definition.match, "<$1>")
        .asText()
        .setBackgroundColor(definition.color);
  }
}

However, not only does it not replace the matched text with <matched text>, but it also replaces the entire background, not just that of the replaced text.

I don't know Javascript well and I've never written a Google Docs add on before.

How do I match tiny snippets of text and set their attributes?

Unlike some other answers on this site, I'm not doing single replacements. Each rule might match against multiple elements in the document. Thus, I'm pretty sure I've messed this up :)

As an aside, some of these documents will be in the 100K word range, so they might be rather large.

TheMaster
  • 45,448
  • 6
  • 62
  • 85
Ovid
  • 11,580
  • 9
  • 46
  • 76
  • Duplicate of https://stackoverflow.com/questions/17863066/why-do-regex-constructors-need-to-be-double-escaped and https://stackoverflow.com/questions/53730641/google-apps-script-greedy-regex it needs to be double escaped for the regex to match and `replaceText()` returns a `Element`: You need to use `FindText` to get `RangeElement` to setBg – TheMaster Nov 01 '20 at 15:14

1 Answers1

1

I changed your example a bit to match what I already had in a document.

Here's the code:

function showIssues() {
  var body = DocumentApp
             .getActiveDocument()
             .getBody();
  
  const rules = {
    one: {
      match: 'Recipe 1',
      color: '#00FF00'
    },
    two: {
      match: 'Recipe 2',
      color: '#FF0000'
    }
  };
  var style1={};
  style1[DocumentApp.Attribute.FOREGROUND_COLOR]='#ff0000';
  for (const [rule, definition] of Object.entries(rules)) {
    let re=body.findText(definition.match).getElement().asText().setAttributes(style1);
  }
}

Before:

enter image description here

After:

enter image description here

Update

I think this is a little closer to what you were attempting.

function showIssues() {
  var body = DocumentApp
             .getActiveDocument()
             .getBody();
  var style1={};
  var style2={};
  style1[DocumentApp.Attribute.FOREGROUND_COLOR]='#ff0000';
  style2[DocumentApp.Attribute.FOREGROUND_COLOR]='#00ff00';
  const rules = {
    one: {
      match: 'Recipe 1',
      color: style1
    },
    two: {
      match: 'Recipe 2',
      color: style2
    }
  };
  var style1={};
  
  for (const [rule, definition] of Object.entries(rules)) {
    let re=body.findText(definition.match).getElement().asText().setAttributes(definition.color);
  }
}

enter image description here

halfer
  • 19,824
  • 17
  • 99
  • 186
Cooper
  • 59,616
  • 6
  • 23
  • 54
  • It gets me closer. Thank you! I have tweaked it and can apply color to a single line of text (which is better) instead of the entire document. However, if there are two matches on a single line, the latter match determines the color. – Ovid Nov 01 '20 at 15:45
  • I believe that you can use the two offsets associate with the element to get just the part that you want. I haven't used this for a while so I'm not sure if it's possible or not – Cooper Nov 01 '20 at 15:49
  • Very, very close. Offsets help, but only works for first match for a given element. So if I have the sentence "This has one and two ", only the first `` is matched. Do you know how I can get a list of matches instead of a single rangeElement (re)? – Ovid Nov 01 '20 at 16:05
  • Without elements you can't set attributes. – Cooper Nov 01 '20 at 16:12
  • Perhaps you can use the [findElement(element type,from)](https://developers.google.com/apps-script/reference/document/body#findElement(ElementType,RangeElement)) – Cooper Nov 01 '20 at 16:13
  • Turns out that this answers my question perfectly. Didn't find it on my first search: https://stackoverflow.com/questions/43741973/apps-script-findtext-for-google-docs – Ovid Nov 01 '20 at 17:13