Skip to content

Typographic substitution while typing (smart quotes + em dash) - #64

Merged
devin-ai-integration[bot] merged 2 commits into
mainfrom
devin/1791301027-smart-quotes
Oct 6, 2026
Merged

devin-ai-integration[bot] merged 2 commits into
mainfrom
devin/1791301027-smart-quotes

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Summary

Adds typographic substitution on the typed-text path of LiveEditor (smart quotes + em dash):

  • "/' become curly by context: opens at line start or after space/(/[/{/—/–, closes otherwise; after a letter or digit ' is always ’ (apostrophes: don’t, d’água).
  • A second - folds -- into —. Lines that are only hyphens/spaces (--- rules, frontmatter fences) or contain | (table rows) stay literal.
  • Backspace right at the end of a substitution restores the straight characters (” → ", — → --) — also the manual escape hatch.
  • Never inside inline code, fenced blocks or frontmatter: smart::literal_at(&Analysis, off) is the single extensible forbidden-context check. Analysis now records metadata ranges for minus_metadata/plus_metadata; math ($…$, PR Add LaTeX math ($…$ / $$…$$): Unicode editor rendering + MathML export #60) plugs into the same function at merge.
  • Paste and programmatic inserts untouched — the substitution only runs in replace_text_in_range for single typed chars.
  • Undo unaffected: substitutions go through self.edit, so “/— undo as one step (undoing — restores the first -).
  • New smart_quotes setting (on by default) persisted in store.rs, with a toggle at the end of Settings → Editor ("Smart quotes" / "Aspas tipográficas").

Core logic is pure and unit-tested: smart::substitute(line, before, typed, on, literal) -> Option<Subst { text, orig, back }> — 6 tests cover open/close, apostrophe, fold, rule/table rejection, literal contexts and the off switch.

GUI-verified (macOS release build)

  • pt-BR + en phrases produce “olá”, ‘respondeu’, “don’t”, it’s and em dashes — byte-checked in the saved file.
  • ---, a | b -- c, `code -- "x"` and a fenced -- "y" and 'z' all stay literal.
  • Backspace reverts — → -- and ” → ".
  • Toggle off → --/"/' stay straight; persists smart_quotes = off and re-enables live.
  • ⌘Z removes a substituted quote as one character.

live demo
phrases and literal contexts
settings toggle

Link to Devin session: https://app.devin.ai/sessions/4c6b312f7f904f8093d1bf890a1cf788
Open in Devin Desktop: https://app.devin.ai/desktop/session/4c6b312f7f904f8093d1bf890a1cf788?variant=devin
Requested by: @horizzon3507

Typing "/' becomes curly by context (open after line start, space or
([{—–; always ’ after a letter/digit), and a second - folds -- into —.
Backspace right after a substitution restores the straight characters,
which also serves as the escape. Lines that are only hyphens/spaces
(markdown rules) or contain | (tables) stay literal, as do inline code,
fenced blocks and frontmatter via a single literal_at() check that new
contexts (e.g. math) can extend. Paste is untouched.

New smart_quotes setting (on by default) persisted in store.rs, with a
toggle at the end of Settings → Editor in en and pt-BR.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

A line typed linearly like `tail -- "z"` used to bake curls into what
becomes a code span, because the tree only flags closed spans. A run of
n backticks opens a span closed by a run of exactly n, so scan the line
prefix in literal_at for an unmatched run (\\-escaped runs skipped).

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Independent GUI verification on the release build (macOS, isolated config, real keystrokes via System Events; all claims byte-checked in the autosaved .md):

Passed: "x" -- y → “x” — y; don't/'x' → don’t/‘x’; --- rule and a | b -- c stay literal; fenced block and closed inline code stay literal (`xx -- "q"`); frontmatter --/" literal while body curls; Backspace restores — → -- and ” → "; ⌘Z undoes each substitution in one step; Settings → Editor "Smart quotes" toggle disables live, persists smart_quotes = off, survives relaunch, and re-enables live (smart_quotes = on).

Edge found and fixed: linear-typed `tail -- "z"` used to produce `tail — “z”` — the chars substituted while the span was still unclosed. Fixed in 1993033: literal_at now also treats an unmatched backtick run in the line prefix as literal, so `fix -- "w"` typed start-to-finish stays straight while prose after the closing backtick still curls.

verification run
prose curly, code literal
frontmatter literal + restores

Settings toggle evidence

Smart quotes toggle
toggled off
still off after relaunch

Written by Devin

@devin-ai-integration
devin-ai-integration Bot merged commit 977b6a8 into main Oct 6, 2026
6 checks passed
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.

1 participant