From ab806dc38ddcd74958b999fe708ae9ea63609f72 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 28 Sep 2026 18:56:04 -0300 Subject: A mouse sweep selects a stream, as acme's does, not a rectangle A B1, B2 or B3 drag over several lines took the same columns from every row, a block. acme's sweep is a stream (libframe/frselect.c:115-140, frselectpaint; acme text.c:881, textselect): the first row from the press to its end, the rows between whole, the last from its start to the release. edit.sweepCols says which columns of a row a sweep takes; the captured spans, the text a sweep yields (copy, chords, B2 and B3 operands, a terminal's automatic yank) and the highlight all follow it, in bodies, multi-row tags, terminals and text-rendered PDFs; a row the sweep runs through is highlighted to the edge, and rows after the first leave out the line numbers. Native PDF selection was already MuPDF's stream. No pane keeps a rectangle. exec-bigdrag's golden changes: its middle sweep now runs whole lines. Co-Authored-By: Claude Opus 5.5 --- test/panes.zig | 60 +++++++++++++++++++----- test/snapshots/exec-bigdrag.golden | 94 +++++++++++++++++++------------------- 2 files changed, 96 insertions(+), 58 deletions(-) (limited to 'test') diff --git a/test/panes.zig b/test/panes.zig index 40b4b6ba..96819728 100644 --- a/test/panes.zig +++ b/test/panes.zig @@ -595,7 +595,8 @@ const BodyHitTests = struct { p.update(.{ .mouse = mouse(layer, last - 1, prefix + 11, .press) }); // Release at a different source row without an intermediate drag event. p.update(.{ .mouse = mouse(layer, last, prefix + 14, .release) }); - try std.testing.expectEqualStrings("body\nbody", pardes.test_api.heldSelection(p, 0).?); + // A stream: the last row from its start, indentation and all. + try std.testing.expect(std.mem.endsWith(u8, pardes.test_api.heldSelection(p, 0).?, "// body") and std.mem.startsWith(u8, pardes.test_api.heldSelection(p, 0).?, "body\n")); p.update(.{ .mouse = mouse(layer, last, 10, .press) }); p.update(.{ .mouse = mouse(layer, last, 10, .release) }); try std.testing.expectEqual(expected, pane.body.cur_row); @@ -873,7 +874,7 @@ const TtySelectionTests = struct { try noChildInput(p); } - test "tty selection preserves exact rectangle through yank clipboard and mode changes" { + test "tty selection preserves its exact stream through yank clipboard and mode changes" { if (comptime !panes.Terminal.enabled) return error.SkipZigTest; for ([_]bool{ false, true }) |prompt| { const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 40, .rows = 12 }); @@ -894,7 +895,7 @@ const TtySelectionTests = struct { const x = rect.x + config.GUTTER; const y = p.bodyTop(pane, rect); JumpSelectionTests.drag(p, x + @as(u16, if (prompt) 0 else 1), y, x + 3, y + @as(u16, if (prompt) 0 else 1)); - const expected = if (prompt) "$ ab" else "lph\nrav"; + const expected = if (prompt) "$ ab" else "lpha\nbrav"; try std.testing.expectEqual(panes.Text.Mode.tty, pane.body.mode); try std.testing.expectEqualStrings(expected, pardes.test_api.heldSelection(p, 0).?); try std.testing.expectEqualStrings(expected, p.registers.text(p.gpa, '"') orelse return error.MissingAutomaticTtyYank); @@ -907,9 +908,13 @@ const TtySelectionTests = struct { try std.testing.expectEqual(panes.Text.Mode.normal, pane.body.mode); const normal = try p.render(frame.allocator()); if (!prompt) { - const selection_bg: pardes.Color = .{ .rgb = p.theme().sel_bg }; - try std.testing.expect(!std.meta.eql(selection_bg, normal.at(x + 4, y).style.bg)); - try std.testing.expect(!std.meta.eql(selection_bg, normal.at(x, y + 1).style.bg)); + // A stream: the first row to its edge, the last from its + // start; (x + 1, y) is surely in it, whatever its tint. + const selected = normal.at(x + 1, y).style.bg; + try std.testing.expect(std.meta.eql(selected, normal.at(x + 4, y).style.bg)); + try std.testing.expect(std.meta.eql(selected, normal.at(x, y + 1).style.bg)); + try std.testing.expect(!std.meta.eql(selected, normal.at(x + 4, y + 1).style.bg)); + try std.testing.expect(!std.meta.eql(selected, normal.at(x, y).style.bg)); } try std.testing.expectEqualStrings(expected, pardes.test_api.heldSelection(p, 0).?); p.update(.{ .key = .{ .cp = 'y' } }); @@ -956,7 +961,7 @@ const TtySelectionTests = struct { const sx = source.x + config.GUTTER; const sy = if (p.settings.tag_bottom) source.y else source.y + pardes.BOX_H; JumpSelectionTests.drag(p, sx + 1, sy, sx + 3, sy + 1); - try std.testing.expectEqualStrings("lph\nrav", p.registers.text(p.gpa, '"') orelse return error.MissingAutomaticTtyYank); + try std.testing.expectEqualStrings("lpha\nbrav", p.registers.text(p.gpa, '"') orelse return error.MissingAutomaticTtyYank); try noChildInput(p); const target = p.rects[1]; @@ -976,13 +981,13 @@ const TtySelectionTests = struct { } const expected = try std.fmt.allocPrint(p.gpa, "{s}{s}", .{ if (reports_mouse) "\x1b[<0;3;1M\x1b[<0;3;1m" else "", - if (bracketed) "\x1b[200~lph\nrav\x1b[201~" else "lph\rrav", + if (bracketed) "\x1b[200~lpha\nbrav\x1b[201~" else "lpha\rbrav", }); defer p.gpa.free(expected); try std.testing.expectEqualStrings(expected, written.written()); try std.testing.expectEqual(panes.Text.Mode.tty, p.panes[0].?.body.mode); try std.testing.expectEqual(panes.Text.Mode.tty, p.panes[1].?.body.mode); - try std.testing.expectEqualStrings("lph\nrav", pardes.test_api.heldSelection(p, 0).?); + try std.testing.expectEqualStrings("lpha\nbrav", pardes.test_api.heldSelection(p, 0).?); } } } @@ -1253,9 +1258,42 @@ const JumpSelectionTests = struct { try std.testing.expectEqual(@as(i32, 0), pane.body.cur_col); } - test "jump selection clears copied rectangles in both history directions" { + test "a B1 sweep over several lines is a stream, as acme's, in a body and a tag" { + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 60, .rows = 12 }); + defer p.deinit(); + const pane = try p.setTestFile("alpha\nbravo\ncharlie\n"); + var frame = std.heap.ArenaAllocator.init(p.gpa); + defer frame.deinit(); + _ = try p.render(frame.allocator()); + const rect = p.rects[0]; + const x = rect.x + config.GUTTER + panes.File.gutterWidth(pane); + const y = p.bodyTop(pane, rect); + // From the press to the end of its line, the line between whole, the + // last from its start: no line numbers, no rectangle. + drag(p, x + 2, y, x + 1, y + 2); + try std.testing.expectEqualStrings("pha\nbravo\nch", pardes.test_api.heldSelection(p, 0).?); + // Upward, the same text. + drag(p, x + 1, y + 2, x + 2, y); + try std.testing.expectEqualStrings("pha\nbravo\nch", pardes.test_api.heldSelection(p, 0).?); + // A tag of several rows sweeps the same way. + if (pane.tag.own) |own| p.gpa.free(own); + pane.tag.own = try p.gpa.dupe(u8, "Save Del\nLook Edit"); + p.sync(); + _ = try p.render(frame.allocator()); + p.sync(); + try std.testing.expectEqual(@as(u16, 2), pane.tag_rows); + const sl: panes.Pane.Sel = .{ .state = .done, .r0 = 0, .c0 = 3, .r1 = 1, .c1 = 1 }; + const text = try pardes.edit.paneText(p, pane); + const swept = try pardes.edit.selectionText(p, pane, sl); + const first_line = text[0 .. std.mem.indexOfScalar(u8, text, '\n') orelse text.len]; + const second = std.mem.sliceTo(text[first_line.len + 1 ..], '\n'); + const want = try std.fmt.allocPrint(frame.allocator(), "{s}\n{s}", .{ first_line[3..], second[0..@min(2, second.len)] }); + try std.testing.expectEqualStrings(want, swept); + } + + test "jump selection clears copied sweeps in both history directions" { for ([_]bool{ false, true }) |reverse| - try exercise("alpha\nbravo\n", false, reverse, 1, 3, "lph\nrav"); + try exercise("alpha\nbravo\n", false, reverse, 1, 3, "lpha\nbrav"); } test "jump selection clears wrapped Unicode and tab rectangles" { diff --git a/test/snapshots/exec-bigdrag.golden b/test/snapshots/exec-bigdrag.golden index 87d3b9bb..c74fd8f9 100644 --- a/test/snapshots/exec-bigdrag.golden +++ b/test/snapshots/exec-bigdrag.golden @@ -2,60 +2,60 @@ |Newcol Joincol Find Grep Help Changelog Tutor Dump NextColor Debug Exit | New Tty Find Grep Joincol Delcol New Tty Find Grep Joincol Delcol | /tmp/pardes-snap/exec-bigdrag/cwd Tty+bash Save Mode Filter Collapse Del /tmp/pardes-snap/exec-bigdrag/cwd Tty+bash Save Mode Filter Collapse Del -| ls bash: line: command not found -| big.txt 4line 2705line 2706line 2707line 2708line 2709line 2710line 2711line 2712line 2713line 2714lin -| bash: 4line: command not found -| 15line 2716line 2717line 2718line 2719line 2720line 2721line 2722line 2723line 2724line 2725li -| bash: 15line: command not found -| 726line 2727line 2728line 2729line 2730line 2731line 2732line 2733line 2734line 2735line 2736l -| bash: 726line: command not found -| 2737line 2738line 2739line 2740line 2741line 2742line 2743line 2744line 2745line 2746line 2747 -| bash: 2737line: command not found -| 2748line 2749line 2750line 2751line 2752line 2753line 2754line 2755line 2756line 2757line 275 -| bash: 2748line: command not found -| e 2759line 2760line 2761line 2762line 2763line 2764line 2765line 2766line 2767line 2768line 27 +| ls 2 +| big.txt bash: 91line: command not found +| 802line 2803line 2804line 2805line 2806line 2807line 2808line 2809line 2810line 2811line 2812lin +| e +| bash: 802line: command not found +| 2813line 2814line 2815line 2816line 2817line 2818line 2819line 2820line 2821line 2822line 2823li +| ne +| bash: 2813line: command not found +| 2824line 2825line 2826line 2827line 2828line 2829line 2830line 2831line 2832line 2833line 2834l +| in +| bash: 2824line: command not found +| e 2835line 2836line 2837line 2838line 2839line 2840line 2841line 2842line 2843line 2844line 2845 +| li | bash: e: command not found -| ne 2770line 2771line 2772line 2773line 2774line 2775line 2776line 2777line 2778line 2779line 2 +| ne 2846line 2847line 2848line 2849line 2850line 2851line 2852line 2853line 2854line 2855line 285 +| 6l | bash: ne: command not found -| ine 2781line 2782line 2783line 2784line 2785line 2786line 2787line 2788line 2789line 2790line +| ine 2857line 2858line 2859line 2860line 2861line 2862line 2863line 2864line 2865line 2866line 28 +| 67 | bash: ine: command not found -| line 2792line 2793line 2794line 2795line 2796line 2797line 2798line 2799line 2800line 2801line +| line 2868line 2869line 2870line 2871line 2872line 2873line 2874line 2875line 2876line 2877line 2 +| 87 | bash: line: command not found -| 2line 2803line 2804line 2805line 2806line 2807line 2808line 2809line 2810line 2811line 2812lin -| bash: 2line: command not found -| 13line 2814line 2815line 2816line 2817line 2818line 2819line 2820line 2821line 2822line 2823li -| bash: 13line: command not found -| 824line 2825line 2826line 2827line 2828line 2829line 2830line 2831line 2832line 2833line 2834l -| bash: 824line: command not found -| 2835line 2836line 2837line 2838line 2839line 2840line 2841line 2842line 2843line 2844line 2845 -| bash: 2835line: command not found -| 2846line 2847line 2848line 2849line 2850line 2851line 2852line 2853line 2854line 2855line 285 -| bash: 2846line: command not found -| /tmp/pardes-snap/exec-bigdrag/cwd Tty+bash Save Mode Filter Collapse Del e 2857line 2858line 2859line 2860line 2861line 2862line 2863line 2864line 2865line 2866line 28 -| ls bash: e: command not found -| big.txt ne 2868line 2869line 2870line 2871line 2872line 2873line 2874line 2875line 2876line 2877line 2 -| bash: ne: command not found -| ine 2879line 2880line 2881line 2882line 2883line 2884line 2885line 2886line 2887line 2888line -| bash: ine: command not found -| liecho alive-o''kne 2890line 2891line 2892line 2893line 2894line 2895line 2896line 2897line 2898li -| bash: line: command not found -| 0line 2901line 2902line 2903line 2904line 2905line 2906line 2907line 2908line 2909line 2910lin -| bash: 0line: command not found -| 11line 2912line 2913line 2914line 2915line 2916line 2917line 2918line 2919line 2920line 2921li -| bash: 11line: command not found -| 922line 2923line 2924line 2925line 2926line 2927line 2928line 2929line 2930line 2931line 2932l -| bash: 922line: command not found -| 2933line 2934line 2935line 2936line 2937line 2938line 2939line 2940line 2941line 2942line 2943 -| bash: 2933line: command not found -| 2944line 2945line 2946line 2947line 2948line 2949line 2950line 2951line 2952line 2953line 295 -| bash: 2944line: command not found -| e 2955line 2956line 2957line 2958line 2959line 2960line 2961line 2962line 2963line 2964line 29 +| 8line 2879line 2880line 2881line 2882line 2883line 2884line 2885line 2886line 2887line 2888line +| 28 +| bash: 8line: command not found +| 89line 2890line 2891line 2892line 2893line 2894line 2895line 2896line 2897line 2898line 2899line +| 2 +| bash: 89line: command not found +| /tmp/pardes-snap/exec-bigdrag/cwd Tty+bash Save Mode Filter Collapse Del 900line 2901line 2902line 2903line 2904line 2905line 2906line 2907line 2908line 2909line 2910lin +| ls e +| big.txt bash: 900line: command not found +| 2911line 2912line 2913line 2914line 2915line 2916line 2917line 2918line 2919line 2920line 2921li +| ne +| bash: 2911line: command not found +| 2echo alive-o''k922line 2923line 2924line 2925line 2926line 2927line 2928line 2929line 2930line 2 +| in +| bash: 2922line: command not found +| e 2933line 2934line 2935line 2936line 2937line 2938line 2939line 2940line 2941line 2942line 2943 +| li | bash: e: command not found -| ne 2966line 2967line 2968line 2969line 2970line 2971line 2972line 2973line 2974line 2975line 2 +| ne 2944line 2945line 2946line 2947line 2948line 2949line 2950line 2951line 2952line 2953line 295 +| 4l | bash: ne: command not found -| ine 2977line 2978line 2979line 2980line 2981line 2982line 2983line 2984line 2985line 2986line +| ine 2955line 2956line 2957line 2958line 2959line 2960line 2961line 2962line 2963line 2964line 29 +| 65 | bash: ine: command not found -| line 2988line 2989line 2990line 2991line 2992line 2993line 2994line 2995line 2996line 2997line +| line 2966line 2967line 2968line 2969line 2970line 2971line 2972line 2973line 2974line 2975line 2 +| 97 | bash: line: command not found +| 6line 2977line 2978line 2979line 2980line 2981line 2982line 2983line 2984line 2985line 2986line +| 29 +| bash: 6line: command not found +| 87line 2988line 2989line 2990line 2991line 2992line 2993line 2994line 2995line 2996line 2997line +| bash: 87line: command not found | echo alive-o''k | alive-ok -- cgit v1.3