Fix: caret x on marker-only list lines in the indent grid - #133
Draft
dylandeheer wants to merge 7 commits into
Draft
Fix: caret x on marker-only list lines in the indent grid#133dylandeheer wants to merge 7 commits into
dylandeheer wants to merge 7 commits into
Conversation
The theme can recolor every element except heading text (it inherits bodyText), and the heading config covers every metric except the face — so an embedder that wants serif display headings over a sans body has to fork. HeadingStyle.fontName picks a PostScript face for headings, honored exactly so the chosen weight survives; an unresolvable name falls back to the stock bold base font, like TaskCheckboxStyle's symbol fallback. MarkdownEditorTheme.headingText colors heading text while the `#` glyphs stay on headingMarker and inline constructs inside a heading keep their own ink. Emphasis inside a heading still composes on top of the configured face (traits added, family and per-level size kept). Both knobs default to nil, which keeps styling identical to before. Co-authored-by: Cursor <cursoragent@cursor.com>
Historically the visual list indent is the raw source whitespace: every first line starts at a flat indentPerLevel, nesting shows only as the advance of the leading spaces/tabs (two spaces = ~8pt -- far off any design grid), and the marker-to-content gap is whatever '- ' happens to measure per marker kind. With markerTextGap set, markers land on a deterministic grid: a level-n item starts at n x indentPerLevel (level 1 on the body origin), the leading whitespace collapses (hidden-marker font; tabs advance by a sub-point interval), and content hangs a fixed slot after the marker for every marker kind via a kern on the final spacer char -- so bullet, any digit count, and task content all align, wrapped lines hang at the content edge, and the caret keeps working on real text advances. The drawn task checkbox left-aligns to the slot origin in grid mode. nil (the default) keeps the historical geometry exactly. Co-authored-by: Cursor <cursoragent@cursor.com>
ListStyle.orderedMarkerStyles formats each nesting depth's painted ordered marker (numeric / alpha / roman, cycling), so nested runs can render 1. / a. / i. while the source keeps its literal digits -- the same source-untouched contract display numbering already follows. A non-numeric depth keeps the overlay active even when the computed number matches the source, since '1.' still has to display as 'a.'; caret and selection reveal the raw digits exactly as before. The default (a single .numeric) keeps every level numeric. MarkdownEditorTheme.listMarker colors the painted bullet and ordered marker glyphs independently of bodyText; nil (the default) keeps the historical body ink. Co-authored-by: Cursor <cursoragent@cursor.com>
…n checkbox to its marker slot Two fixes to the opt-in indent grid observed in a real render: - Grid depth now comes from the item's position in the run's indent ladder (seeded across scoped-restyle windows like the ordered display numbers) instead of a fixed 2-columns-per-level divisor, so CommonMark ordered nesting (3+ columns per level, driven by the parent marker width) steps exactly one level per parent. Legacy (nil markerTextGap) geometry is untouched. - A hidden task in the grid now collapses its whole `- [ ] ` prefix so the box range's own position is the marker-slot origin, and the drawn square left-aligns there (previously the square was offset from the box chars, which sit AFTER the full-advance `- `, pushing it to negative x where the container clipped it). Co-authored-by: Cursor <cursoragent@cursor.com>
…er style The per-depth ordered marker style now reads the structural nesting depth (indent-ladder position) instead of a spaces/2 divisor, so CommonMark ordered nesting (3 columns per level) picks the next style exactly one level per parent — `1.` / `a.` / `i.` on levels 1/2/3. This branch now builds on feature/list-indent-grid, which introduces the shared depth computation. Co-authored-by: Cursor <cursoragent@cursor.com>
…indent grid Every marker kind occupies an invisible fixed-width column at its depth indent: painted bullets and ordered markers center horizontally in the column (the ordered overlay always paints in this mode so the source digits' left alignment can't leak), and the drawn task checkbox fills the column exactly — its square side becomes the slot width, shared by draw and hit-test. Content hangs markerSlotWidth + markerTextGap after the marker origin, so checkbox, bullet, and number items read as one alignment grid. nil (the default) keeps the gap-only grid geometry exactly. Co-authored-by: Cursor <cursoragent@cursor.com>
A fresh list continuation (`- `, `1. `, `- [ ] ` with no content yet) carries its content offset as a kern on the final spacer character, and Core Text drops the kern on the last glyph of a line — so while the item is still empty the insertion point blinks at the raw collapsed-marker advance (the line's left edge, in front of the drawn marker) while the first typed character lands a full slot away. Snap the insertion indicator to the paragraph's headIndent, which in grid geometry IS the content origin (depth × indentPerLevel + marker slot), whenever the caret sits at the end of a marker-only list line. Detection reuses MarkdownLists.listRegex (the Enter-continuation recognizer). Legacy (nil markerTextGap) geometry is untouched: content advance there comes from real glyph advances and the caret already lands on the content origin. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
On the opt-in indent grid (#123/#131), a fresh list continuation (
-,1.,- [ ]with no content yet) rendered the insertion point at the line's left edge — in front of the drawn marker — while the first typed character lands a full marker slot to the right.Why
The grid carries the content offset as a kern on the final spacer character, and Core Text drops the kern on the last glyph of a line. On a marker-only line the spacer IS the last glyph, so the caret rect collapses back to the raw collapsed-marker advance (~x=0) until content exists.
How
NativeTextView+MarkerOnlyCaret.swiftsnaps theNSTextInsertionIndicatorto the paragraph'sheadIndent— which in grid geometry is exactly the content origin (depth ×indentPerLevel+ marker slot) — whenever the caret sits at the end of a marker-only list line. Detection reusesMarkdownLists.listRegex(the same recognizer the Enter-continuation logic uses), and the snap plugs into the existing caret-workaround pipeline (updateInsertionPointStateAndRestartTimer+ the indicator frame KVO).Legacy (nil
markerTextGap) geometry is untouched: content advance there comes from real glyph advances, not an end-of-line kern, and the caret already lands on the content origin (covered by a test).Defaults
No behavior change unless
lists.markerTextGapis configured (the grid itself is opt-in and off by default).Tests
MarkerOnlyCaretTests: task/bullet/ordered continuations snap to headIndent, nested depth steps, and no-correction guards (content lines, mid-syntax carets, plain text, legacy geometry, selections, helpers off). Full suite green.Note: stacked on
feature/list-marker-slot(#131); the unique diff is the last commit.Made with Cursor