Skip to content

Fix syntax errors, fix formatting bugs, and fix code vanishing issue due to stale cache#293

Open
Soumyajit7 wants to merge 4 commits into
alexwforsythe:mainfrom
Soumyajit7:fix-issues
Open

Fix syntax errors, fix formatting bugs, and fix code vanishing issue due to stale cache#293
Soumyajit7 wants to merge 4 commits into
alexwforsythe:mainfrom
Soumyajit7:fix-issues

Conversation

@Soumyajit7

@Soumyajit7 Soumyajit7 commented Jun 4, 2026

Copy link
Copy Markdown

Fix execution errors, XML parsing failures, and data-loss bug

This PR addresses several major issues preventing the add-on from functioning properly in modern Google Docs environments, including a critical data-loss bug where code would vanish upon formatting.

1. Migrated away from deprecated Rhino runtime
Google has officially deprecated the older Rhino runtime for Apps Script.

  • Updated appsscript.json to properly specify the V8 runtime environment to resolve the The Rhino runtime is deprecated and no longer supported execution failure.

2. Fixed SAXParseException ("Content is not allowed in prolog")
The underlying XmlService.parse() was failing strictly due to invisible characters and malformed characters hitting the parser.

  • Encoding Fix: The dist/styles.html file generated during the build pipeline occasionally contained UTF-16LE invisible Byte Order Marks (BOM). I added regex to strip any leading whitespace and BOMs before parsing.
  • Sanitization: Escaped standard HTML entities like   to   prior to XML parsing to prevent unescaped entities from crashing the strict XML parser.
  • Updated jQuery parsing in client/sidebar.js to strictly filter for element nodes, dropping loose whitespace text nodes that were previously polluting the <pre> element payload sent to the server.

3. Fixed "Code Vanishing" bug (Stale Cache Issue)
Users were experiencing a major issue where clicking "Format" on a fresh sidebar load would successfully delete their highlighted code, but insert an empty invisible table in its place.

  • The Cause: Google Apps Script's CacheService stores the hash of the last highlighted text. If a user reloaded the sidebar and highlighted the same text, the server would see a cache match and tell the client "the selection hasn't changed, I will blindly insert the preview HTML you passed me." However, on a fresh sidebar load, the preview HTML is an empty <pre id="preview"></pre> placeholder.
  • The Fix: Added strict server-side payload validation in insertCodeOrGetSelectionAndThemeCss inside server/main.js. The server will no longer blindly trust the cache match if the client payload is missing the expected highlight.js structural classes, forcing a safe regeneration instead.

Summary by Sourcery

Fix XML parsing and caching issues that caused formatting to fail or remove code blocks in the Google Docs add-on.

Bug Fixes:

  • Normalize and sanitize generated HTML before XML parsing to prevent SAX parse failures from stray whitespace, BOMs, and non-escaped entities.
  • Ensure only element nodes are used when constructing highlighted blocks on the client to avoid malformed payloads being sent for insertion.
  • Guard cache-based insertion logic to require valid highlight.js HTML before reusing cached selections, preventing empty code tables from replacing user code.
  • Correct document attribute mapping so style properties are reliably applied via DocumentApp attributes in the V8 runtime.

Build:

  • Add acorn and acorn-walk runtime dependencies required for the updated Apps Script environment.

@sourcery-ai

sourcery-ai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Reviewer's Guide

Updates server- and client-side logic to run on V8, harden XML/HTML handling, and prevent stale cached previews from causing code-loss when formatting highlighted text.

Sequence diagram for insertCodeOrGetSelectionAndThemeCss cache validation and safe insertion

sequenceDiagram
    actor User
    participant Sidebar
    participant Server
    participant Document

    User->>Sidebar: click Format
    Sidebar->>Server: insertCodeOrGetSelectionAndThemeCss(html, prefs)

    Server->>Server: hasValidHtml = html && html.indexOf('class="hljs') !== -1
    Server->>Server: selection = getSelection()
    Server->>Server: selectedText = getTextFromSelection(selection)

    alt hasValidHtml && alreadySelected(selectedText) && alreadySaved(prefs)
        Server->>Document: insertCode(html, prefs.noBackground, selection)
        Server-->>Sidebar: confirm insertion
    else cache invalid or missing highlight
        Server->>Server: [regenerate preview HTML]
        Server-->>Sidebar: return new preview HTML and theme CSS
    end
Loading

File-Level Changes

Change Details Files
Sanitize and normalize HTML before XML parsing to avoid parser errors and safely map CSS styles to DocumentApp attributes.
  • Trim leading/trailing whitespace and BOM-like characters from incoming HTML before XmlService.parse
  • Escape   to numeric entity form prior to XML parsing
  • Map document style attributes using string keys and convert them through DocumentApp.Attribute at use time
  • Guard against falsy HTML payloads in replaceSelection and skip processing when absent
server/document.js
server/constants.js
Ensure client-side highlighted HTML is well-formed element content before sending to the server.
  • Use jQuery trim on highlighted HTML before parsing
  • Filter parsed HTML nodes to element nodes only, dropping whitespace text nodes
  • Call defineThirdPartyGrammars instead of previously used language definition function to align with current highlighting setup
client/sidebar.js
Add validation around cached HTML previews so stale or empty content is not reinserted into the document.
  • Detect presence of expected highlight.js class markers in incoming HTML
  • Require both valid HTML and matching selection/preferences before reusing cached preview HTML
  • Fall back to regenerating highlighted code when HTML is missing or invalid
server/main.js
Align project configuration and dependencies with current Apps Script and build tooling requirements.
  • Add acorn and acorn-walk as runtime dependencies
  • Update appsscript.json to target the V8 runtime (details in diff outside snippet)
  • Regenerate package-lock.json to capture new dependencies and runtime config
package.json
appsscript.json
package-lock.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The new trimming/sanitization in replaceSelection and createHighlightedBlock removes all leading/trailing whitespace (including &nbsp;), which may unintentionally strip meaningful indentation or trailing spaces in code samples; consider narrowing the sanitization to only what breaks XML parsing (e.g., BOMs) or limiting it to outer wrapper elements.
  • The hasValidHtml check in insertCodeOrGetSelectionAndThemeCss relies on the literal substring class="hljs", which could fail if classes are reordered or single quotes are used; consider a more robust validation (e.g., regex for hljs as a class token or parsing the root node) to avoid false negatives.
  • In createHighlightedBlock, $.parseHTML(highlighted) is now filtered to element nodes only, but you still treat block as a single jQuery collection; if multiple root elements can be produced, clarify whether that is expected and, if not, explicitly select or wrap the intended root element.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new trimming/sanitization in `replaceSelection` and `createHighlightedBlock` removes all leading/trailing whitespace (including `&nbsp;`), which may unintentionally strip meaningful indentation or trailing spaces in code samples; consider narrowing the sanitization to only what breaks XML parsing (e.g., BOMs) or limiting it to outer wrapper elements.
- The `hasValidHtml` check in `insertCodeOrGetSelectionAndThemeCss` relies on the literal substring `class="hljs"`, which could fail if classes are reordered or single quotes are used; consider a more robust validation (e.g., regex for `hljs` as a class token or parsing the root node) to avoid false negatives.
- In `createHighlightedBlock`, `$.parseHTML(highlighted)` is now filtered to element nodes only, but you still treat `block` as a single jQuery collection; if multiple root elements can be produced, clarify whether that is expected and, if not, explicitly select or wrap the intended root element.

## Individual Comments

### Comment 1
<location path="server/document.js" line_range="338" />
<code_context>
+                attrs[DocumentApp.Attribute[attrName]] = true;
             }
             return;
         case constants.document.cssAttrs.background:
</code_context>
<issue_to_address>
**issue (bug_risk):** Background attribute key is now inconsistent with the new string-based docAttrs mapping.

For `background`, `constants.document.docAttrs.background` now resolves to the string `'BACKGROUND_COLOR'`, so `attrs[constants.document.docAttrs.background] = val;` uses a plain string key, unlike the other branches which use `DocumentApp.Attribute[attrName]`. For consistency and correct typing, this should also use `DocumentApp.Attribute[constants.document.docAttrs.background]` (or keep `docAttrs.background` as the enum instead of a string).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread server/document.js
attrs[DocumentApp.Attribute[attrName]] = true;
}
return;
case constants.document.cssAttrs.background:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Background attribute key is now inconsistent with the new string-based docAttrs mapping.

For background, constants.document.docAttrs.background now resolves to the string 'BACKGROUND_COLOR', so attrs[constants.document.docAttrs.background] = val; uses a plain string key, unlike the other branches which use DocumentApp.Attribute[attrName]. For consistency and correct typing, this should also use DocumentApp.Attribute[constants.document.docAttrs.background] (or keep docAttrs.background as the enum instead of a string).

…prove hljs regex validation, and wrap multi-root highlighted blocks
@pointmatic

Copy link
Copy Markdown

@Soumyajit7 did you take a look at the PR a few days before yours?

#275

@Soumyajit7

Copy link
Copy Markdown
Author

@pointmatic No I did not look at those PR, I took fork from master branch and tried to fix the issue, and it's working now.

- Updated title in server/constants.js from 'Code Blocks' to 'New Code Blocks'
- Added addOns section to appsscript.json to register as a Google Docs add-on
- Added build_win.sh helper script for building on Windows via Git Bash
@alexwforsythe alexwforsythe deleted the branch alexwforsythe:main June 10, 2026 20:41
@alexwforsythe alexwforsythe reopened this Jun 10, 2026
@alexwforsythe alexwforsythe self-requested a review as a code owner June 10, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants