From a30b6ede69ddadf15cd242cdac5742db26ae5758 Mon Sep 17 00:00:00 2001 From: Twice Date: Sun, 11 Oct 2026 06:39:55 +0000 Subject: [PATCH] Render inline code without raised background --- internal/tui2/prose/inline.go | 7 ++++--- internal/tui3/markdown.go | 37 ++++++++++++++++++++-------------- internal/tui3/markdown_test.go | 10 +++++---- internal/tui3/pathlink_test.go | 8 +++++--- internal/tui3/tasklink_test.go | 8 ++++---- 5 files changed, 41 insertions(+), 29 deletions(-) diff --git a/internal/tui2/prose/inline.go b/internal/tui2/prose/inline.go index b8665ed336..9e6fdf7b2e 100644 --- a/internal/tui2/prose/inline.go +++ b/internal/tui2/prose/inline.go @@ -119,8 +119,9 @@ func (r *renderer) codeSpan(n *ast.CodeSpan, w *wrapper, st style) { next := st next.tok = tokens.TextPrimary // ONE VISIBLE MARK PER TOKEN. The callback carries semantic knowledge prose - // does not own; when it says another mark is coming, adding the plane here - // would turn one token into the loudest object in its sentence. + // does not own; when it says another mark is coming, prose still renders the + // text plainly and lets that later mark carry the affordance. Ordinary inline + // code is plain too: code fences and semantic highlights own the raised plane. if r.opts.PlainCodeSpan != nil && r.opts.PlainCodeSpan(text) { w.push(text, next) return @@ -130,7 +131,7 @@ func (r *renderer) codeSpan(n *ast.CodeSpan, w *wrapper, st style) { w.push("`"+text+"`", next) return } - next.ground = true + next.tok = tokens.TextSecondary w.push(text, next) } diff --git a/internal/tui3/markdown.go b/internal/tui3/markdown.go index e2e2365191..24a6fe228f 100644 --- a/internal/tui3/markdown.go +++ b/internal/tui3/markdown.go @@ -813,10 +813,10 @@ func wrapCodeLine(line string, width int) []string { // // CODE IS NOT PROSE AND GETS NO LINKS. Two guards cover the code that can name // a task: a fenced block carries [tokens.GlyphCodeGutter], while a non-path -// inline span carries prose's raised plane. Path spans deliberately drop that -// plane under ONE VISIBLE MARK PER TOKEN, but the linker's path grammar cannot -// recognize their whitespace-bearing task reference anyway. At profiles with -// no plane, prose restores backticks and [masked] reads those instead. +// inline span carries prose's quieter inline-code ink. Path spans deliberately +// render plain under ONE VISIBLE MARK PER TOKEN, but the linker's path grammar +// cannot recognize their whitespace-bearing task reference anyway. At profiles +// with no plane, prose restores backticks and [masked] reads those instead. // taskLink is one drawn reference: the columns it occupies on its row, and the // node behind them. @@ -864,7 +864,7 @@ func linkifyTasks(text string, pal palette, look func(uint64) (string, bool), ho if !hasTaskWord(text) { return text, nil } - flat, ground := flatten(text) + flat, mask := flatten(text) if strings.Contains(flat, tokens.GlyphCodeGutter) { // A fenced line. The whole row is source, and source that says "task 7" // is saying it to a compiler. @@ -873,7 +873,7 @@ func linkifyTasks(text string, pal palette, look func(uint64) (string, bool), ho refs := taskRefs(flat) kept := refs[:0] for _, ref := range refs { - if grounded(ground, ref.from, ref.to) || masked(flat, ref.from) { + if grounded(mask, ref.from, ref.to) || masked(flat, ref.from) { continue } title, ok := look(ref.id) @@ -1068,29 +1068,29 @@ func indexFold(s, needle string, from int) int { // ── the painted row, taken apart and put back together ────────────────────── // flatten strips one painted row to its plain bytes and reports, byte for byte, -// whether it was drawn on a background. +// whether it belongs to a span task links must ignore. // // It does its own stripping rather than calling [ansi.Strip] for one reason: the // two answers have to be indexed the same way, and the only guarantee of that is // that one walk produced both. func flatten(text string) (string, []bool) { var ( - out strings.Builder - ground []bool - on bool + out strings.Builder + mask []bool + on bool ) out.Grow(len(text)) for i := 0; i < len(text); { if n := escLen(text, i); n > 0 { - on = sgrGround(text[i:i+n], on) + on = sgrTaskMask(text[i:i+n], on) i += n continue } out.WriteByte(text[i]) - ground = append(ground, on) + mask = append(mask, on) i++ } - return out.String(), ground + return out.String(), mask } // paintLinks writes the row back out with every reference inked, and returns the @@ -1268,14 +1268,21 @@ func escLen(s string, i int) int { } // sgrGround folds one escape sequence into "is there a background under this". -func sgrGround(seq string, on bool) bool { +func sgrTaskMask(seq string, on bool) bool { params, ok := sgrParams(seq) if !ok { return on } + body := strings.TrimPrefix(seq, "\x1b[") + body = strings.TrimSuffix(body, "m") + inlineCode := strings.TrimPrefix(tokens.TextSecondary.Fg(tokens.TrueColor, tokens.FocusNormal), "\x1b[") + inlineCode = strings.TrimSuffix(inlineCode, "m") + if body == inlineCode { + on = true + } for i := 0; i < len(params); i++ { switch param := params[i]; { - case param == "", param == "0", param == "49": + case param == "", param == "0", param == "39", param == "49": on = false case param == "48": on = true diff --git a/internal/tui3/markdown_test.go b/internal/tui3/markdown_test.go index 37c6e7db24..4bca414fa3 100644 --- a/internal/tui3/markdown_test.go +++ b/internal/tui3/markdown_test.go @@ -237,10 +237,12 @@ func TestMarkdownEmphasisIsWeightNotPunctuation(t *testing.T) { if !strings.Contains(row, "\x1b[3m") { t.Fatalf("italic lost its slant: %q", row) } - // An inline code span is set apart by its ground, not by its punctuation: - // the raised plane where the profile has one. - if sheet := tokens.Sheet.Bg(tokens.TrueColor, tokens.FocusNormal); !strings.Contains(row, sheet) { - t.Fatalf("the code span is not raised onto the sheet: %q", row) + // An inline code span is set apart without its punctuation or a raised plane. + if sheet := tokens.Sheet.Bg(tokens.TrueColor, tokens.FocusNormal); strings.Contains(row, sheet) { + t.Fatalf("the code span is raised onto the sheet: %q", row) + } + if code := tokens.TextSecondary.Fg(tokens.TrueColor, tokens.FocusNormal) + "code"; !strings.Contains(row, code) { + t.Fatalf("the code span is not distinguished as secondary text: %q", row) } } diff --git a/internal/tui3/pathlink_test.go b/internal/tui3/pathlink_test.go index 4c19502194..483ef1d4f0 100644 --- a/internal/tui3/pathlink_test.go +++ b/internal/tui3/pathlink_test.go @@ -219,15 +219,17 @@ func TestInlineCodePathsAndCodeSpendOneMarkEach(t *testing.T) { row := rows[0] sheet := tokens.Sheet.Bg(tokens.TrueColor, tokens.FocusNormal) path := pal.underline("internal/tui3/pathlink.go") - code := sheet + tokens.TextPrimary.Fg(tokens.TrueColor, tokens.FocusNormal) + "len(x)" if !strings.Contains(row, path) { t.Fatalf("the path does not wear the palette underline: %q", row) } if strings.Contains(row, sheet+tokens.TextPrimary.Fg(tokens.TrueColor, tokens.FocusNormal)+"internal/tui3/pathlink.go") { t.Fatalf("the path also wears the raised plane: %q", row) } - if !strings.Contains(row, code) { - t.Fatalf("ordinary inline code lost the raised plane: %q", row) + if strings.Contains(row, sheet) { + t.Fatalf("inline code kept the raised code plane: %q", row) + } + if !strings.Contains(row, "len(x)") { + t.Fatalf("ordinary inline code disappeared: %q", row) } if strings.Count(row, sgrUnderOn) != 1 { t.Fatalf("ordinary inline code also wears an underline: %q", row) diff --git a/internal/tui3/tasklink_test.go b/internal/tui3/tasklink_test.go index d8ee31103c..9a3bb5bb81 100644 --- a/internal/tui3/tasklink_test.go +++ b/internal/tui3/tasklink_test.go @@ -113,8 +113,8 @@ func TestTheTaskLinkGrammarRecognizesOnlyWhatNamesATask(t *testing.T) { } // CODE IS NOT PROSE AND GETS NO LINKS — both ways task-shaped code reaches a -// row: the fenced block behind its gutter, and the non-path inline span on the -// raised plane. +// row: the fenced block behind its gutter, and the non-path inline span in +// prose's quieter inline-code ink. func TestTaskLinksSkipCode(t *testing.T) { look := func(uint64) (string, bool) { return "Fix the nil-map", true } pal := newPalette(tokens.ANSI256, false) @@ -126,8 +126,8 @@ func TestTaskLinksSkipCode(t *testing.T) { t.Fatalf("a fenced line grew %d links: %q", len(links), plain(out)) } - // AN INLINE SPAN, through the real renderer: a code span is drawn on the - // surface's one raised plane, and this pass reads the plane off the row. + // AN INLINE SPAN, through the real renderer: a code span is drawn without + // the raised plane, and this pass still tells it apart from prose. st := tokens.NewStyler(tokens.TrueColor, tokens.FocusNormal) if !tokens.TrueColor.SheetGround() { t.Skip("this profile has no raised plane to test")