this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: correct forward-word to land at end of word, not end of separator

Address code review finding: moveForwardWordwise had inverted loop
order (skip word, skip non-word) which overshoots past separators.
Corrected to (skip non-word, skip non-word) matching readline's
forward-word ("move forward to the end of the next word").

Also sync missing test cases between TextInput and TextField, and
clean up rambling test comments.

authored by

Mike Bannister and committed by
Tim Culverhouse
c09eb895 37b7d895

+48 -25
+32 -6
src/vxfw/TextField.zig
··· 365 365 } 366 366 367 367 /// Moves the cursor forward by one word using character-class boundaries. 368 - /// Skips word characters, then skips non-word characters (matching readline forward-word). 368 + /// Skips non-word characters, then skips word characters — landing at the end of the next word 369 + /// (matching readline forward-word). 369 370 pub fn moveForwardWordwise(self: *TextField) void { 370 371 const second_half = self.buf.secondHalf(); 371 372 var i: usize = 0; 373 + // Skip non-word characters 374 + while (i < second_half.len and !isWordChar(second_half[i])) : (i += 1) {} 372 375 // Skip word characters 373 376 while (i < second_half.len and isWordChar(second_half[i])) : (i += 1) {} 374 - // Skip non-word characters 375 - while (i < second_half.len and !isWordChar(second_half[i])) : (i += 1) {} 376 377 self.buf.moveGapRight(i); 377 378 } 378 379 ··· 635 636 try std.testing.expectEqualStrings("hello-world", input.buf.secondHalf()); 636 637 } 637 638 638 - test "moveForwardWordwise stops at word boundary" { 639 + test "moveForwardWordwise stops at end of word" { 639 640 var input = TextField.init(std.testing.allocator); 640 641 defer input.deinit(); 641 642 try input.insertSliceAtCursor("hello-world"); 642 643 input.buf.moveGapLeft(input.buf.firstHalf().len); 643 644 input.moveForwardWordwise(); 644 - try std.testing.expectEqualStrings("hello-", input.buf.firstHalf()); 645 - try std.testing.expectEqualStrings("world", input.buf.secondHalf()); 645 + // Stops at end of "hello": "hello|-world" 646 + try std.testing.expectEqualStrings("hello", input.buf.firstHalf()); 647 + try std.testing.expectEqualStrings("-world", input.buf.secondHalf()); 646 648 input.moveForwardWordwise(); 649 + // Skips "-" then stops at end of "world": "hello-world|" 647 650 try std.testing.expectEqualStrings("hello-world", input.buf.firstHalf()); 648 651 try std.testing.expectEqualStrings("", input.buf.secondHalf()); 649 652 } ··· 696 699 try std.testing.expectEqualStrings(" baz", input.buf.secondHalf()); 697 700 } 698 701 702 + test "moveForwardWordwise with dots" { 703 + var input = TextField.init(std.testing.allocator); 704 + defer input.deinit(); 705 + try input.insertSliceAtCursor("foo.bar.baz"); 706 + input.buf.moveGapLeft(input.buf.firstHalf().len); 707 + input.moveForwardWordwise(); 708 + try std.testing.expectEqualStrings("foo", input.buf.firstHalf()); 709 + input.moveForwardWordwise(); 710 + try std.testing.expectEqualStrings("foo.bar", input.buf.firstHalf()); 711 + input.moveForwardWordwise(); 712 + try std.testing.expectEqualStrings("foo.bar.baz", input.buf.firstHalf()); 713 + } 714 + 699 715 test "word motion with underscores treats them as word chars" { 700 716 var input = TextField.init(std.testing.allocator); 701 717 defer input.deinit(); 702 718 try input.insertSliceAtCursor("hello_world-test"); 703 719 input.moveBackwardWordwise(); 704 720 try std.testing.expectEqualStrings("hello_world-", input.buf.firstHalf()); 721 + input.moveBackwardWordwise(); 722 + try std.testing.expectEqualStrings("", input.buf.firstHalf()); 723 + } 724 + 725 + test "word motion with spaces" { 726 + var input = TextField.init(std.testing.allocator); 727 + defer input.deinit(); 728 + try input.insertSliceAtCursor("hello world"); 729 + input.moveBackwardWordwise(); 730 + try std.testing.expectEqualStrings("hello ", input.buf.firstHalf()); 705 731 input.moveBackwardWordwise(); 706 732 try std.testing.expectEqualStrings("", input.buf.firstHalf()); 707 733 }
+16 -19
src/widgets/TextInput.zig
··· 284 284 } 285 285 286 286 /// Moves the cursor forward by one word using character-class boundaries. 287 - /// Skips word characters, then skips non-word characters (matching readline forward-word). 287 + /// Skips non-word characters, then skips word characters — landing at the end of the next word 288 + /// (matching readline forward-word). 288 289 pub fn moveForwardWordwise(self: *TextInput) void { 289 290 const second_half = self.buf.secondHalf(); 290 291 var i: usize = 0; 292 + // Skip non-word characters 293 + while (i < second_half.len and !isWordChar(second_half[i])) : (i += 1) {} 291 294 // Skip word characters 292 295 while (i < second_half.len and isWordChar(second_half[i])) : (i += 1) {} 293 - // Skip non-word characters 294 - while (i < second_half.len and !isWordChar(second_half[i])) : (i += 1) {} 295 296 self.buf.moveGapRight(i); 296 297 } 297 298 ··· 476 477 try std.testing.expectEqualStrings("hello-world", input.buf.secondHalf()); 477 478 } 478 479 479 - test "moveForwardWordwise stops at word boundary" { 480 + test "moveForwardWordwise stops at end of word" { 480 481 var input = TextInput.init(std.testing.allocator); 481 482 defer input.deinit(); 482 483 try input.insertSliceAtCursor("hello-world"); ··· 484 485 input.buf.moveGapLeft(input.buf.firstHalf().len); 485 486 // Cursor at start: "|hello-world" 486 487 input.moveForwardWordwise(); 487 - // Should skip "hello" then "-": "hello-|world" 488 - try std.testing.expectEqualStrings("hello-", input.buf.firstHalf()); 489 - try std.testing.expectEqualStrings("world", input.buf.secondHalf()); 488 + // Should stop at end of "hello": "hello|-world" 489 + try std.testing.expectEqualStrings("hello", input.buf.firstHalf()); 490 + try std.testing.expectEqualStrings("-world", input.buf.secondHalf()); 490 491 input.moveForwardWordwise(); 491 - // Should skip "world" to end: "hello-world|" 492 + // Should skip "-" then stop at end of "world": "hello-world|" 492 493 try std.testing.expectEqualStrings("hello-world", input.buf.firstHalf()); 493 494 try std.testing.expectEqualStrings("", input.buf.secondHalf()); 494 495 } ··· 513 514 try input.insertSliceAtCursor("foo.bar.baz"); 514 515 input.buf.moveGapLeft(input.buf.firstHalf().len); 515 516 input.moveForwardWordwise(); 516 - try std.testing.expectEqualStrings("foo.", input.buf.firstHalf()); 517 + // Stops at end of "foo": "foo|.bar.baz" 518 + try std.testing.expectEqualStrings("foo", input.buf.firstHalf()); 517 519 input.moveForwardWordwise(); 518 - try std.testing.expectEqualStrings("foo.bar.", input.buf.firstHalf()); 520 + // Skips "." then stops at end of "bar": "foo.bar|.baz" 521 + try std.testing.expectEqualStrings("foo.bar", input.buf.firstHalf()); 519 522 input.moveForwardWordwise(); 523 + // Skips "." then stops at end of "baz": "foo.bar.baz|" 520 524 try std.testing.expectEqualStrings("foo.bar.baz", input.buf.firstHalf()); 521 525 } 522 526 ··· 550 554 defer input.deinit(); 551 555 try input.insertSliceAtCursor("foo.bar baz"); 552 556 input.buf.moveGapLeft(input.buf.firstHalf().len); 553 - // Cursor at start: "|foo.bar baz" 554 557 input.deleteWordAfter(); 555 - // Should delete "foo" then ".": ".bar baz" — wait, readline kill-word skips non-word then word 556 - // Actually: skip non-word (none at start), skip word "foo" = delete "foo" 557 - // No: kill-word skips word chars first, then non-word chars? Let me think... 558 - // readline forward-kill-word: delete forward to end of next word 559 - // From "|foo.bar": delete "foo" (word chars), then "." (non-word chars) = "foo." 560 - // Actually no. readline kill-word: skip non-word, then skip word. Same as our deleteWordAfter. 561 - // From "|foo.bar": no non-word to skip, then skip "foo" = delete "foo" leaving ".bar baz" 558 + // kill-word: skip non-word (none), skip word "foo" → delete "foo" 562 559 try std.testing.expectEqualStrings("", input.buf.firstHalf()); 563 560 try std.testing.expectEqualStrings(".bar baz", input.buf.secondHalf()); 564 561 input.deleteWordAfter(); 565 - // From "|.bar baz": skip "." (non-word), then skip "bar" (word) = delete ".bar" 562 + // kill-word: skip "." (non-word), skip "bar" (word) → delete ".bar" 566 563 try std.testing.expectEqualStrings("", input.buf.firstHalf()); 567 564 try std.testing.expectEqualStrings(" baz", input.buf.secondHalf()); 568 565 }