100 Useful Google Docs Apps Script Functions (That Every Docs Power-User Should Know)

Google Docs + Apps Script is a magic combo for automating writing workflows, templates, formatting, and reporting.

https://github.com/lsvekis/100-Useful-Google-Docs-Apps-Script-Functions

In this post, you’ll get 100 tiny utility functions you can drop into your own projects. Each one is:

  • Focused on Google Docs use-cases
  • Small and reusable
  • Uses built-in DocumentApp and DriveApp (no advanced APIs required)

Tip: Create a single .gs file called docsHelpers.gs and paste all the functions there. Then call them from your main script files.


🧱 Section 1 – Core Setup & Helpers

1. Get the active document

function getActiveDoc_() {
  return DocumentApp.getActiveDocument();
}

2. Get the body of the active document

function getBody_() {
  return DocumentApp.getActiveDocument().getBody();
}

3. Get document title

function getDocTitle_() {
  return DocumentApp.getActiveDocument().getName();
}

4. Set document title

function setDocTitle_(newTitle) {
  DocumentApp.getActiveDocument().setName(newTitle);
}

5. Log basic document info

function logDocInfo_() {
  const doc = DocumentApp.getActiveDocument();
  console.log('ID:', doc.getId());
  console.log('Title:', doc.getName());
  console.log('URL:', doc.getUrl());
}

📄 Section 2 – Create & Open Documents

6. Create a new document with title

function createDoc_(title) {
  return DocumentApp.create(title || 'New Document');
}

7. Create a doc and write first line

function createDocWithText_(title, firstLine) {
  const doc = DocumentApp.create(title);
  doc.getBody().appendParagraph(firstLine || 'Hello from Apps Script!');
  return doc;
}

8. Open a document by ID

function openDocById_(id) {
  return DocumentApp.openById(id);
}

9. Open a document by URL

function openDocByUrl_(url) {
  return DocumentApp.openByUrl(url);
}

10. Duplicate an existing document

function copyDoc_(docId, newName) {
  const file = DriveApp.getFileById(docId);
  return file.makeCopy(newName || (file.getName() + ' (Copy)'));
}

✍️ Section 3 – Basic Text Insertion

11. Append a paragraph to the end of the doc

function appendParagraph_(text) {
  getBody_().appendParagraph(text);
}

12. Insert text at the top

function insertAtTop_(text) {
  const body = getBody_();
  body.insertParagraph(0, text);
}

13. Append plain text (no new paragraph)

function appendTextToLastParagraph_(text) {
  const body = getBody_();
  const last = body.getParagraphs().slice(-1)[0];
  last.appendText(text);
}

14. Append a heading

function appendHeading_(text, level) {
  const body = getBody_();
  const para = body.appendParagraph(text);
  const style = DocumentApp.ParagraphHeading['HEADING' + (level || 1)];
  para.setHeading(style);
}

15. Insert a horizontal rule

function insertHorizontalRule_() {
  getBody_().appendHorizontalRule();
}

🎨 Section 4 – Text Styling

16. Make all text bold

function makeAllBold_() {
  const body = getBody_();
  body.editAsText().setBold(true);
}

17. Make selection bold

function toggleSelectionBold_() {
  const doc = getActiveDoc_();
  const range = doc.getSelection();
  if (!range) return;
  range.getRangeElements().forEach(re => {
    const el = re.getElement();
    if (el.editAsText) {
      const text = el.editAsText();
      text.setBold(re.getStartOffset(), re.getEndOffsetInclusive(), true);
    }
  });
}

18. Set document default font

function setDefaultFont_(fontName) {
  getBody_().editAsText().setFontFamily(fontName || 'Arial');
}

19. Set font size of whole document

function setAllFontSize_(size) {
  getBody_().editAsText().setFontSize(size || 11);
}

20. Highlight all headings

function highlightHeadings_(color) {
  const body = getBody_();
  body.getParagraphs().forEach(p => {
    if (p.getHeading() !== DocumentApp.ParagraphHeading.NORMAL) {
      p.setBackgroundColor(color || '#FFF3CD');
    }
  });
}

🔍 Section 5 – Find & Replace

21. Simple find text

function findOccurrences_(search) {
  const body = getBody_();
  const found = body.findText(search);
  return found;
}

22. Find all occurrences and log

function findAllAndLog_(search) {
  const body = getBody_();
  let result = body.findText(search);
  while (result) {
    console.log('Found at', result.getStartOffset());
    result = body.findText(search, result);
  }
}

23. Replace first occurrence

function replaceFirst_(search, replacement) {
  const body = getBody_();
  const result = body.findText(search);
  if (result) {
    const text = result.getElement().asText();
    text.replaceText(search, replacement);
  }
}

24. Replace all occurrences

function replaceAll_(search, replacement) {
  const body = getBody_();
  body.replaceText(search, replacement);
}

25. Case-insensitive replace all

function replaceAllCaseInsensitive_(search, replacement) {
  const body = getBody_();
  const pattern = new RegExp(search, 'gi');
  body.replaceText(pattern, replacement);
}

📑 Section 6 – Paragraphs & Structure

26. Count paragraphs

function countParagraphs_() {
  return getBody_().getParagraphs().length;
}

27. Get all paragraph texts

function getAllParagraphTexts_() {
  return getBody_().getParagraphs().map(p => p.getText());
}

28. Delete empty paragraphs

function removeEmptyParagraphs_() {
  const body = getBody_();
  const paras = body.getParagraphs();
  for (let i = paras.length - 1; i >= 0; i--) {
    if (!paras[i].getText().trim()) {
      body.removeChild(paras[i]);
    }
  }
}

29. Convert all text to normal (remove headings)

function resetAllHeadingsToNormal_() {
  getBody_().getParagraphs().forEach(p => {
    p.setHeading(DocumentApp.ParagraphHeading.NORMAL);
  });
}

30. Turn each line into a bulleted list

function convertToBulletedList_() {
  const body = getBody_();
  const paras = body.getParagraphs();
  paras.forEach(p => p.setBullet(true));
}

31. Remove bullets from all paragraphs

function removeAllBullets_() {
  getBody_().getParagraphs().forEach(p => p.removeFromList());
}

32. Indent all paragraphs by one level

function indentAll_() {
  getBody_().getParagraphs().forEach(p => p.indentStart(36)); // 36pt approx
}

33. Set line spacing for all paragraphs

function setLineSpacing_(spacing) {
  getBody_().getParagraphs().forEach(p => p.setLineSpacing(spacing || 1.15));
}

34. Add page break after each heading

function pageBreakAfterHeadings_() {
  const body = getBody_();
  const paras = body.getParagraphs();
  for (let i = paras.length - 1; i >= 0; i--) {
    if (paras[i].getHeading() !== DocumentApp.ParagraphHeading.NORMAL) {
      body.insertPageBreak(i + 1);
    }
  }
}

35. Insert page break at cursor

function insertPageBreakAtCursor_() {
  const doc = getActiveDoc_();
  const cursor = doc.getCursor();
  if (!cursor) return;
  const el = cursor.insertPageBreak();
}

📚 Section 7 – Headings & Table of Contents

36. Set first paragraph as Title

function setFirstAsTitle_() {
  const body = getBody_();
  const first = body.getParagraphs()[0];
  first.setHeading(DocumentApp.ParagraphHeading.TITLE);
}

37. Promote all headings one level (H3→H2, etc.)

function promoteHeadings_() {
  const body = getBody_();
  body.getParagraphs().forEach(p => {
    const h = p.getHeading();
    if (h === DocumentApp.ParagraphHeading.HEADING2) {
      p.setHeading(DocumentApp.ParagraphHeading.HEADING1);
    } else if (h === DocumentApp.ParagraphHeading.HEADING3) {
      p.setHeading(DocumentApp.ParagraphHeading.HEADING2);
    }
  });
}

38. Demote headings one level

function demoteHeadings_() {
  const body = getBody_();
  body.getParagraphs().forEach(p => {
    const h = p.getHeading();
    if (h === DocumentApp.ParagraphHeading.HEADING1) {
      p.setHeading(DocumentApp.ParagraphHeading.HEADING2);
    } else if (h === DocumentApp.ParagraphHeading.HEADING2) {
      p.setHeading(DocumentApp.ParagraphHeading.HEADING3);
    }
  });
}

39. Add a table of contents at top

function insertTOC_() {
  const body = getBody_();
  body.insertParagraph(0, 'Table of Contents')
      .setHeading(DocumentApp.ParagraphHeading.HEADING1);
  body.insertTable(1, [[DocumentApp.ElementType.TABLE_OF_CONTENTS]]);
}

40. Remove all table of contents elements

function removeAllTOC_() {
  const body = getBody_();
  const numChildren = body.getNumChildren();
  for (let i = numChildren - 1; i >= 0; i--) {
    const child = body.getChild(i);
    if (child.getType() === DocumentApp.ElementType.TABLE_OF_CONTENTS) {
      body.removeChild(child);
    }
  }
}

📝 Section 8 – Lists

41. Turn selection into a bulleted list

function selectionToBullets_() {
  const doc = getActiveDoc_();
  const sel = doc.getSelection();
  if (!sel) return;
  sel.getRangeElements().forEach(re => {
    const p = re.getElement().getParent().asParagraph();
    p.setBullet(true);
  });
}

42. Turn selection into a numbered list

function selectionToNumberedList_() {
  const doc = getActiveDoc_();
  const sel = doc.getSelection();
  if (!sel) return;
  sel.getRangeElements().forEach(re => {
    const p = re.getElement().getParent().asParagraph();
    p.setGlyphType(DocumentApp.GlyphType.NUMBER);
  });
}

43. Remove list formatting from selection

function clearListFromSelection_() {
  const doc = getActiveDoc_();
  const sel = doc.getSelection();
  if (!sel) return;
  sel.getRangeElements().forEach(re => {
    const p = re.getElement().getParent().asParagraph();
    p.removeFromList();
  });
}

44. Increase indent level for list items

function indentListItems_() {
  getBody_().getListItems().forEach(li => li.indentStart(36));
}

45. Decrease indent level for list items

function outdentListItems_() {
  getBody_().getListItems().forEach(li => li.indentStart(0));
}

🖼️ Section 9 – Images

46. Insert image from URL at end

function insertImageFromUrl_(url) {
  const blob = UrlFetchApp.fetch(url).getBlob();
  getBody_().appendImage(blob);
}

47. Insert image at cursor

function insertImageAtCursor_(url) {
  const doc = getActiveDoc_();
  const cursor = doc.getCursor();
  if (!cursor) return;
  const blob = UrlFetchApp.fetch(url).getBlob();
  cursor.insertInlineImage(blob);
}

48. Resize all images (width)

function resizeAllImages_(width) {
  const body = getBody_();
  const imgs = body.getImages();
  imgs.forEach(img => img.setWidth(width || 300));
}

49. Add caption under each image

function addImageCaptions_(prefix) {
  const body = getBody_();
  const imgs = body.getImages();
  imgs.forEach((img, index) => {
    const parent = img.getParent().getParent();
    const caption = body.insertParagraph(
      body.getChildIndex(parent) + 1,
      (prefix || 'Figure') + ' ' + (index + 1)
    );
    caption.setItalic(true);
  });
}

50. Count images in document

function countImages_() {
  return getBody_().getImages().length;
}

📊 Section 10 – Tables

51. Insert a simple 3×3 table at cursor or end

function insertSimpleTable_() {
  const body = getBody_();
  const table = body.appendTable();
  for (let r = 0; r < 3; r++) {
    const row = table.appendTableRow();
    for (let c = 0; c < 3; c++) {
      row.appendTableCell(`R${r+1}C${c+1}`);
    }
  }
}

52. Insert table from 2D array

function insertTableFromArray_(data) {
  const body = getBody_();
  const table = body.appendTable();
  data.forEach(rowData => {
    const row = table.appendTableRow();
    rowData.forEach(cell => row.appendTableCell(String(cell)));
  });
}

53. Style table header row

function styleTableHeader_() {
  const body = getBody_();
  body.getTables().forEach(table => {
    const headerRow = table.getRow(0);
    for (let i = 0; i < headerRow.getNumCells(); i++) {
      const cell = headerRow.getCell(i);
      cell.setBackgroundColor('#F3F4F6');
      cell.editAsText().setBold(true);
    }
  });
}

54. Auto-size all table columns (simple approach)

function clearTableCellAttributes_() {
  getBody_().getTables().forEach(table => {
    for (let r = 0; r < table.getNumRows(); r++) {
      const row = table.getRow(r);
      for (let c = 0; c < row.getNumCells(); c++) {
        row.getCell(c).clear();
      }
    }
  });
}

(Docs doesn’t give fine-grained column width control via DocumentApp, but clearing attributes often lets Docs auto-size.)

55. Add border color to all tables

function setTableBorderColor_(color) {
  getBody_().getTables().forEach(table => {
    for (let r = 0; r < table.getNumRows(); r++) {
      const row = table.getRow(r);
      for (let c = 0; c < row.getNumCells(); c++) {
        row.getCell(c).setBorderColorTop(color || '#000000')
                      .setBorderColorBottom(color || '#000000')
                      .setBorderColorLeft(color || '#000000')
                      .setBorderColorRight(color || '#000000');
      }
    }
  });
}

📎 Section 11 – Links & Bookmarks

56. Add hyperlink to selected text

function addLinkToSelection_(url) {
  const doc = getActiveDoc_();
  const sel = doc.getSelection();
  if (!sel) return;
  sel.getRangeElements().forEach(re => {
    const el = re.getElement();
    if (el.editAsText) {
      el.editAsText().setLinkUrl(re.getStartOffset(), re.getEndOffsetInclusive(), url);
    }
  });
}

57. Clear all links in document

function clearAllLinks_() {
  const text = getBody_().editAsText();
  text.setLinkUrl(null);
}

58. Insert bookmark at cursor

function insertBookmarkAtCursor_() {
  const doc = getActiveDoc_();
  const cursor = doc.getCursor();
  if (!cursor) return;
  doc.addBookmark(cursor);
}

59. List all bookmarks

function listBookmarks_() {
  const doc = getActiveDoc_();
  const bookmarks = doc.getBookmarks();
  bookmarks.forEach(b => console.log(b.getId(), b.getPosition().getElement().getText()));
}

60. Remove all bookmarks

function removeAllBookmarks_() {
  const doc = getActiveDoc_();
  doc.getBookmarks().forEach(b => doc.removeBookmark(b));
}

📄 Section 12 – Headers, Footers & Page Setup

61. Ensure header exists

function ensureHeader_() {
  const body = getBody_();
  if (!body.getHeader()) {
    body.setHeader(body.insertParagraph(0, ''));
  }
}

62. Set header text

function setHeaderText_(text) {
  const body = getBody_();
  let header = body.getHeader();
  if (!header) {
    body.setHeader(body.insertParagraph(0, ''));
    header = body.getHeader();
  }
  header.clear();
  header.appendParagraph(text);
}

63. Set footer text

function setFooterText_(text) {
  const body = getBody_();
  let footer = body.getFooter();
  if (!footer) {
    body.setFooter(body.appendParagraph(''));
    footer = body.getFooter();
  }
  footer.clear();
  footer.appendParagraph(text);
}

64. Set page orientation to landscape

function setLandscape_() {
  getBody_().setPageOrientation(DocumentApp.PageOrientation.LANDSCAPE);
}

65. Set page orientation to portrait

function setPortrait_() {
  getBody_().setPageOrientation(DocumentApp.PageOrientation.PORTRAIT);
}

66. Insert page number placeholder in footer (simple text)

function insertPageNumberPlaceholder_() {
  setFooterText_('Page {{pageNumber}}');
}

(Docs doesn’t support dynamic page numbers via DocumentApp, but this can act as a placeholder for templates.)


🧩 Section 13 – Selections & Cursors

67. Log current cursor position

function logCursorPosition_() {
  const doc = getActiveDoc_();
  const cursor = doc.getCursor();
  if (!cursor) {
    console.log('No cursor');
    return;
  }
  console.log(cursor.getElement().getText());
}

68. Wrap selection in quotation marks

function quoteSelection_() {
  const doc = getActiveDoc_();
  const sel = doc.getSelection();
  if (!sel) return;
  sel.getRangeElements().forEach(re => {
    const el = re.getElement();
    if (el.editAsText) {
      const text = el.editAsText();
      const s = re.getStartOffset();
      const e = re.getEndOffsetInclusive();
      const selected = text.getText().substring(s, e + 1);
      text.deleteText(s, e);
      text.insertText(s, '"' + selected + '"');
    }
  });
}

69. Uppercase selection

function upperCaseSelection_() {
  const doc = getActiveDoc_();
  const sel = doc.getSelection();
  if (!sel) return;
  sel.getRangeElements().forEach(re => {
    const el = re.getElement();
    if (el.editAsText) {
      const text = el.editAsText();
      const s = re.getStartOffset();
      const e = re.getEndOffsetInclusive();
      const selected = text.getText().substring(s, e + 1);
      text.deleteText(s, e);
      text.insertText(s, selected.toUpperCase());
    }
  });
}

70. Lowercase selection

function lowerCaseSelection_() {
  const doc = getActiveDoc_();
  const sel = doc.getSelection();
  if (!sel) return;
  sel.getRangeElements().forEach(re => {
    const el = re.getElement();
    if (el.editAsText) {
      const text = el.editAsText();
      const s = re.getStartOffset();
      const e = re.getEndOffsetInclusive();
      const selected = text.getText().substring(s, e + 1);
      text.deleteText(s, e);
      text.insertText(s, selected.toLowerCase());
    }
  });
}

🔄 Section 14 – Template Placeholders

71. Replace simple placeholders like {{NAME}}

function replacePlaceholder_(key, value) {
  const body = getBody_();
  body.replaceText('{{' + key + '}}', value);
}

72. Fill multiple placeholders from an object

function fillTemplate_(data) {
  const body = getBody_();
  Object.keys(data).forEach(key => {
    body.replaceText('{{' + key + '}}', String(data[key]));
  });
}

73. Clear all placeholders ({{…}})

function clearAllPlaceholders_() {
  const body = getBody_();
  body.replaceText(/\{\{.*?\}\}/g, '');
}

74. Highlight all placeholders

function highlightPlaceholders_(color) {
  const body = getBody_();
  let result = body.findText(/\{\{.*?\}\}/);
  while (result) {
    const text = result.getElement().asText();
    const s = result.getStartOffset();
    const e = result.getEndOffsetInclusive();
    text.setBackgroundColor(s, e, color || '#FFF3CD');
    result = body.findText(/\{\{.*?\}\}/, result);
  }
}

75. Generate a document from template ID

function createFromTemplate_(templateId, newName, data) {
  const file = DriveApp.getFileById(templateId);
  const copy = file.makeCopy(newName || (file.getName() + ' Filled'));
  const doc = DocumentApp.openById(copy.getId());
  const body = doc.getBody();
  Object.keys(data || {}).forEach(key => {
    body.replaceText('{{' + key + '}}', String(data[key]));
  });
  doc.saveAndClose();
  return copy;
}

📂 Section 15 – Drive & Sharing Helpers

76. Get doc file object

function getDocFile_() {
  const doc = getActiveDoc_();
  return DriveApp.getFileById(doc.getId());
}

77. Move doc to a folder by ID

function moveDocToFolder_(folderId) {
  const file = getDocFile_();
  const folder = DriveApp.getFolderById(folderId);
  const parents = file.getParents();
  while (parents.hasNext()) {
    parents.next().removeFile(file);
  }
  folder.addFile(file);
}

78. Share doc with a user (viewer)

function shareDocView_(email) {
  getDocFile_().addViewer(email);
}

79. Share doc with a user (editor)

function shareDocEdit_(email) {
  getDocFile_().addEditor(email);
}

80. Remove a user’s access

function removeUserAccess_(email) {
  const file = getDocFile_();
  file.removeEditor(email);
  file.removeViewer(email);
}

🧹 Section 16 – Cleanup & Formatting Utilities

81. Trim extra spaces in document

function trimSpaces_() {
  const text = getBody_().editAsText();
  const cleaned = text.getText().replace(/[ \t]+/g, ' ');
  text.setText(cleaned);
}

82. Remove double blank lines

function removeDoubleBlankLines_() {
  const text = getBody_().editAsText();
  const cleaned = text.getText().replace(/\n{3,}/g, '\n\n');
  text.setText(cleaned);
}

83. Normalize quotes (“smart” to plain)

function normalizeQuotes_() {
  const text = getBody_().editAsText();
  const t = text.getText()
    .replace(/[“”]/g, '"')
    .replace(/[‘’]/g, "'");
  text.setText(t);
}

84. Remove trailing spaces at line ends

function removeTrailingSpaces_() {
  const text = getBody_().editAsText();
  const t = text.getText().replace(/[ \t]+$/gm, '');
  text.setText(t);
}

85. Convert all tabs to 4 spaces

function tabsToSpaces_() {
  const text = getBody_().editAsText();
  text.setText(text.getText().replace(/\t/g, '    '));
}

🗂️ Section 17 – Export & Integration

86. Get document as PDF blob

function getPdfBlob_() {
  const file = getDocFile_();
  return file.getAs('application/pdf');
}

87. Email document as PDF

function emailDocAsPdf_(email, subject, body) {
  const pdf = getPdfBlob_();
  MailApp.sendEmail({
    to: email,
    subject: subject || 'Document PDF',
    body: body || 'Please find attached.',
    attachments: [pdf]
  });
}

88. Get document plain text

function getDocPlainText_() {
  return getBody_().getText();
}

89. Export document as plain text to Drive

function saveDocAsTxt_() {
  const file = getDocFile_();
  const name = file.getName() + '.txt';
  DriveApp.createFile(name, getBody_().getText(), MimeType.PLAIN_TEXT);
}

90. Export doc as HTML (simple)

function saveDocAsHtml_() {
  const file = getDocFile_();
  const blob = file.getBlob();
  DriveApp.createFile(file.getName() + '.html', blob.getDataAsString(), MimeType.HTML);
}

(This is a rough export; for full fidelity use Drive API exports.)


🧰 Section 18 – Menus & UI

91. Add a custom menu

function onOpen() {
  DocumentApp.getUi()
    .createMenu('Doc Tools')
    .addItem('Clean up double blank lines', 'removeDoubleBlankLines_')
    .addItem('Trim spaces', 'trimSpaces_')
    .addSeparator()
    .addItem('Insert TOC', 'insertTOC_')
    .addToUi();
}

92. Show a basic alert

function showAlert_(msg) {
  DocumentApp.getUi().alert(msg || 'Done!');
}

93. Prompt user for placeholder value

function fillSinglePlaceholderWithPrompt_() {
  const ui = DocumentApp.getUi();
  const resp = ui.prompt('Placeholder', 'Enter value for {{NAME}}', ui.ButtonSet.OK_CANCEL);
  if (resp.getSelectedButton() === ui.Button.OK) {
    replacePlaceholder_('NAME', resp.getResponseText());
    ui.alert('Updated {{NAME}}');
  }
}

94. Confirmation before running clean-up

function confirmAndClean_() {
  const ui = DocumentApp.getUi();
  const resp = ui.alert('Cleanup', 'Run cleanup on this document?', ui.ButtonSet.YES_NO);
  if (resp === ui.Button.YES) {
    trimSpaces_();
    removeDoubleBlankLines_();
    ui.alert('Cleanup complete');
  }
}

95. Simple sidebar with instructions

function showSidebar_() {
  const html = HtmlService.createHtmlOutput('<p>Welcome to Doc Tools!</p>')
    .setTitle('Doc Tools');
  DocumentApp.getUi().showSidebar(html);
}

⚙️ Section 19 – Triggers & Logging

96. Log when doc is opened (via installable trigger)

function onOpenLogger_(e) {
  console.log('Document opened at', new Date());
}

(Install as an installable trigger tied to the document.)

97. Auto-clean on edit (for specific workflows)

function onEditDoc_(e) {
  // Not a simple Docs trigger – you’d use onOpen + manual call
  trimSpaces_();
}

(Docs doesn’t support a simple onEdit like Sheets; this is more of a pattern placeholder.)

98. Timestamp footer update

function updateFooterTimestamp_() {
  const now = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy-MM-dd HH:mm');
  setFooterText_('Last updated: ' + now);
}

99. Save doc then log size

function saveAndLogSize_() {
  const doc = getActiveDoc_();
  doc.saveAndClose();
  const file = getDocFile_();
  console.log('File size (bytes):', file.getSize());
}

100. Master “daily cleanup” function

function dailyDocCleanup_() {
  trimSpaces_();
  removeDoubleBlankLines_();
  removeTrailingSpaces_();
  normalizeQuotes_();
  updateFooterTimestamp_();
  showAlert_('Daily cleanup complete!');
}

✅ How to Use These Functions

  1. In your Google Doc, go to Extensions → Apps Script.
  2. Create a file named docsHelpers.gs.
  3. Paste in the functions you want from this post.
  4. Save, then reload the Doc to trigger onOpen() menus.
  5. Run functions from:
    • The Run menu in the editor, or
    • Your new custom menu in the Doc (after onOpen runs).

You now have a toolbox of 100 common Google Docs Apps Script helpers you can mix, match, and extend for your own workflows, templates, and automations.