-
Notifications
You must be signed in to change notification settings - Fork 12
Don't insert stray markers when formatting with no selection (CS-11697) #5673
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1011,13 +1011,11 @@ function wrapWith(marker: string) { | |
| let { from, to } = view.state.selection.main; | ||
| let len = marker.length; | ||
|
|
||
| // No selection: insert an empty pair of markers and drop the cursor | ||
| // between them so the user can type the content (e.g. **|**). | ||
| // No selection: do nothing. Inserting an empty marker pair here leaves | ||
| // stray markers (e.g. **) visible in source and preview. Inline formatting | ||
| // applies to selected text only — the toolbar disables these buttons and | ||
| // the keyboard shortcut is a no-op until the user highlights something. | ||
| if (from === to) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Claude Code 🤖] Confirmation: returning Returning When this would stop being safe: if a lower-priority keymap ever wants to handle Mod-B/Mod-I/Mod- |
||
| view.dispatch({ | ||
| changes: { from, insert: marker + marker }, | ||
| selection: { anchor: from + len }, | ||
| }); | ||
| return true; | ||
| } | ||
|
|
||
|
|
@@ -1099,12 +1097,8 @@ function toggleLink(view: EditorView): boolean { | |
| } | ||
|
|
||
| if (from === to) { | ||
| // No selection: insert empty link syntax with the cursor inside the | ||
| // brackets so the user can type the link text — [|](url). | ||
| view.dispatch({ | ||
| changes: { from, insert: '[](url)' }, | ||
| selection: { anchor: from + 1 }, | ||
| }); | ||
| // No selection: do nothing. Inserting empty link syntax leaves a stray | ||
| // [](url) in the document. Linking applies to selected text only. | ||
| return true; | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Claude Code 🤖] Regression (minor, non-blocking): unlinking with a collapsed cursor is no longer reachable from the UI.
The mechanism.
toggleLinkinpackages/host/app/lib/codemirror-context.tsresolves the syntax tree and checks for an enclosingLinknode before its collapsed-selection check, and a collapsed cursor inside[text](url)satisfiesfrom >= link.from && to <= link.to— so previously, placing the caret in a link and clicking the Link button unlinked it. ThisrequiresSelection: truedisables the button whenever the selection is collapsed, and the toolbar button istoggleLink's only caller (the markdown keymap binds only Mod-B / Mod-I / Mod-`), so the collapsed-cursor entry into the unlink branch is now dead in practice.Verified. Traced the base-branch
toggleLink: a collapsed caret inside a link takes the unlink branch (it precedesif (from === to)) and dispatches the text replacement. Grepped all non-test callers:toggleLinkis invoked only via_toggleLinkin this file.What still works. Selecting any range within the link enables the button, and that selection still satisfies the unlink guard — unlink remains reachable, it just now demands a selection. That's why this is minor.
The way out — two options, either fine:
toggleLink— "Linking applies to selected text only" isn't quite true of the function itself, whose earlier branch still unlinks at a collapsed caret; it's only true of what the UI now lets through.SelectionInfo— note the update listener increateEditorStatehardcodesformatsto all-false for collapsed selections, soformats.linkcan't serve as-is.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Claude Code 🤖] Resolved by restoring the affordance (option 2). What changed:
findEnclosingLinkis extracted fromtoggleLink's unlink branch and shared with the selection-info emitter increateEditorState, so the toolbar's Link-button state is computed by the exact same syntax-tree check the command runs — the two can't drift.formats.linkfrom that helper (the other formats stay false, matchingwrapWith's no-op at a caret), and the toolbar's enablement rule gains an escape: an active toggle stays clickable without a selection, since untoggling works at a bare caret.toggleLink's no-op comment is fixed — that branch is now correctly described as "caret outside any link".Coverage added: a
toggleLinkunlink-from-bare-caret command test, a selection-info test pinningformats.linkat a caret inside vs. outside a link, and a component test asserting the Link button'saria-pressedflips as the caret enters and leaves a link (drivable headless — only enablement needs OS focus). The pre-existing selection-based unlink test also now calls the realtoggleLinkinstead of simulating it with a string scan. Both touched modules pass: codemirror-context 55/55, RichMarkdownField 30/30.