Skip to content

fix: Unescape %% in Text when not using String.format()#332

Open
claudeaceae wants to merge 1 commit intoskiptools:mainfrom
claudeaceae:fix/percent-sign-markdown
Open

fix: Unescape %% in Text when not using String.format()#332
claudeaceae wants to merge 1 commit intoskiptools:mainfrom
claudeaceae:fix/percent-sign-markdown

Conversation

@claudeaceae
Copy link

Summary

Fixes #331. Literal % characters in LocalizedStringKey patterns are escaped to %% for Java String.format() safety in appendLiteral(), but this escaping leaked to display paths that don't call String.format(), causing doubled percent signs in the output.

For example, Text("Perfect! 100% **bold**") would display as Perfect! 100%% bold on Android because:

  1. appendLiteral escapes %%% in the pattern
  2. Markdown text nodes without format specifiers bypass String.format()
  3. The %% is returned and displayed as-is

Changes

This adds %%% unescaping in three code paths where String.format() is not used:

  • localizedTextString() — when the interpolations list is empty (the !interpolations.isEmpty() check skips formatting)
  • Render() non-markdown path — when interpolations is nil
  • Markdown text/code/link nodes — via a new displayString(for:interpolations:) helper that checks interpolationIndexes to determine if formattedString() went through String.format() or returned the raw string

The unescaping is safe because:

  • When String.format() WAS called, it already converts %%%, so there are no %% sequences left to unescape
  • When String.format() was NOT called, %% must be unescaped to restore the original %

Note

The root escaping in appendLiteral() is preserved because it's needed when String.format() IS called (e.g., Text("100% \(name)")). A deeper refactor could move the escaping to the formatting layer instead, but this targeted fix is lower risk.

Test plan

  • Verify Text("Perfect! 100% **bold**") displays correctly on Android (should show 100% not 100%%)
  • Verify Text("100% \(name)") with interpolation still works correctly
  • Verify Text("100% done") without markdown displays correctly
  • Verify Text("Use %% for modulo") preserves double percent when intended

🤖 Generated with Claude Code

Fixes skiptools#331. Literal % characters in LocalizedStringKey are escaped to %%
for Java String.format() safety, but this escaping leaked to display paths
that don't use String.format(), causing doubled percent signs in output.

This adds unescaping of %% back to % in three code paths:
- localizedTextString() when interpolations list is empty
- Render() non-markdown path when interpolations is nil
- Markdown text/code/link nodes that have no format specifiers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@cla-bot
Copy link

cla-bot bot commented Feb 11, 2026

Thank you for your pull request and welcome to the Skip community. We require contributors to sign our contributor license agreement (CLA), and we don't seem to have the user(s) @claudeaceae on file. In order for us to review and merge your code, for each noted user please add your GitHub username to Skip's .clabot file

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.

[Android] Text with % and markdowns inside

1 participant

Comments