-- SPDX-FileCopyrightText: © 2026 Vladimir Zorin -- SPDX-License-Identifier: OWL-1.0 or later local testimony = require("testimony") local buffer = require("lem.buffer") local motion = require("lem.motion") local testify = testimony.new("== lem: motion ==") local new_buf = function(text) local buf = buffer.new() buf:insert(0, text) return buf end testify:that("find_char_forward defaults count to 1", function() local buf = new_buf("abxcxdx") local line, col = motion.find_char_forward(buf, 1, 1, "x", false, 8) testimony.assert_equal(1, line) testimony.assert_equal(3, col) end) testify:that("find_char_forward honors count", function() local buf = new_buf("abxcxdx") local line, col = motion.find_char_forward(buf, 1, 1, "x", false, 8, 2) testimony.assert_equal(1, line) testimony.assert_equal(5, col) end) testify:that("find_char_forward till lands before counted match", function() local buf = new_buf("abxcxdx") local line, col = motion.find_char_forward(buf, 1, 1, "x", true, 8, 2) testimony.assert_equal(1, line) testimony.assert_equal(4, col) end) testify:that("find_char_backward honors count", function() local buf = new_buf("axbxcdx") local line, col = motion.find_char_backward(buf, 1, 7, "x", false, 8, 2) testimony.assert_equal(1, line) testimony.assert_equal(2, col) end) testify:that("find_char_backward till lands after counted match", function() local buf = new_buf("axbxcdx") local line, col = motion.find_char_backward(buf, 1, 7, "x", true, 8, 2) testimony.assert_equal(1, line) testimony.assert_equal(3, col) end) testify:that("find_char motions return nil when match count is unavailable", function() local buf = new_buf("abxc") local line = motion.find_char_forward(buf, 1, 1, "x", false, 8, 2) testimony.assert_nil(line) line = motion.find_char_backward(buf, 1, 4, "a", false, 8, 2) testimony.assert_nil(line) end) testify:that("word_forward moves across words punctuation and blank lines", function() local buf = new_buf("alpha, beta\n\n gamma\n") local line, col = motion.word_forward(buf, 1, 1, 1, false, 8) testimony.assert_equal(1, line) testimony.assert_equal(6, col) line, col = motion.word_forward(buf, 1, 1, 2, false, 8) testimony.assert_equal(1, line) testimony.assert_equal(8, col) line, col = motion.word_forward(buf, 1, 1, 3, false, 8) testimony.assert_equal(2, line) testimony.assert_equal(1, col) end) testify:that("word_forward big treats punctuation as part of words", function() local buf = new_buf("alpha, beta\n") local line, col = motion.word_forward(buf, 1, 1, 1, true, 8) testimony.assert_equal(1, line) testimony.assert_equal(8, col) end) testify:that("word_backward crosses blank lines and stops at bof", function() local buf = new_buf("alpha beta\n\n gamma\n") local line, col = motion.word_backward(buf, 3, 7, 1, false, 8) testimony.assert_equal(3, line) testimony.assert_equal(3, col) -- An empty line is a word stop (vim); leading whitespace is not line, col = motion.word_backward(buf, 3, 7, 2, false, 8) testimony.assert_equal(2, line) testimony.assert_equal(1, col) line, col = motion.word_backward(buf, 1, 1, 1, false, 8) testimony.assert_equal(1, line) testimony.assert_equal(1, col) end) testify:that("word_end reaches current next words and handles whitespace lines", function() local buf = new_buf("alpha beta\n \ngamma\n") local line, col = motion.word_end(buf, 1, 1, 1, false, 8) testimony.assert_equal(1, line) testimony.assert_equal(5, col) line, col = motion.word_end(buf, 1, 1, 2, false, 8) testimony.assert_equal(1, line) testimony.assert_equal(10, col) line, col = motion.word_end(buf, 3, 5, 1, false, 8) testimony.assert_equal(4, line) testimony.assert_equal(1, col) end) testify:that("search_backward wraps around to matches below the cursor", function() local buf = new_buf("alpha\nbeta\nneedle here\n") -- Cursor on line 1: the only match is on line 3, reachable via wrap local line, col = motion.search_backward(buf, 1, 1, "needle", 8) testimony.assert_equal(3, line) testimony.assert_equal(1, col) end) testify:that("search_backward on the cursor line only matches before the cursor", function() local buf = new_buf("xx yy xx\n") local line, col = motion.search_backward(buf, 1, 7, "xx", 8) testimony.assert_equal(1, line) testimony.assert_equal(1, col) end) testify:that("paragraph motions land on blank lines", function() local buf = new_buf("one\ntwo\n\nthree\nfour\n\nfive\n") -- } from inside the first paragraph stops at the blank line 3 testimony.assert_equal(3, motion.paragraph_forward(buf, 1, 1)) -- } again: from the blank line, through paragraph two, to blank line 6 testimony.assert_equal(6, motion.paragraph_forward(buf, 3, 1)) -- { from inside paragraph two goes back to blank line 3 testimony.assert_equal(3, motion.paragraph_backward(buf, 5, 1)) -- { from the first paragraph stops at line 1 testimony.assert_equal(1, motion.paragraph_backward(buf, 2, 1)) end) testify:that("word_forward lands on first non-blank of an indented next line", function() local buf = new_buf("alpha\n gamma\n") local line, col = motion.word_forward(buf, 1, 1, 1, false, 8) testimony.assert_equal(2, line) testimony.assert_equal(3, col) end) testify:that("word_forward skips whitespace-only lines", function() local buf = new_buf("alpha\n \nbeta\n") local line, col = motion.word_forward(buf, 1, 1, 1, false, 8) testimony.assert_equal(3, line) testimony.assert_equal(1, col) end) testify:that("word_end crosses consecutive blank lines", function() local buf = new_buf("alpha\n\n\nbeta\n") local line, col = motion.word_end(buf, 1, 5, 1, false, 8) testimony.assert_equal(4, line) testimony.assert_equal(4, col) end) testify:conclude()