diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..02dc863 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.todo.md diff --git a/README.md b/README.md index 6fbafb1..7134678 100644 --- a/README.md +++ b/README.md @@ -370,6 +370,10 @@ require("dial.config").augends:register_group{ |`augend.constant.alias.alpha` |Lowercase alphabet letter (word) |`a`, `b`, `c`, ..., `z` | |`augend.constant.alias.Alpha` |Uppercase alphabet letter (word) |`A`, `B`, `C`, ..., `Z` | |`augend.semver.alias.semver` |Semantic version |`0.3.0`, `1.22.1`, `3.9.1`, ... | +|`augend.ordinal.alias.en` |English shortform ordinal numbers |`0th`, `1st`, `2nd`, `3rd`, `4th`, ... | +|`augend.ordinal.alias.en_neg` |English shortform ordinal numbers including negatives |..., `-1st`, `0th`, `1st`, `2nd`, `3rd`, `4th`, ... | +|`augend.ordinal.alias.en_old` |English shortform ordinal numbers - archaic |`0th`, `1st`, `2d`, `3d`, `4th`, ... | +|`augend.misc.alias.markdown_header` |ATX-Style markdown headings |`# This is a title`, `### Notes` | If you don't specify any settings, the following augends is set as the value of the `default` group. diff --git a/doc/dial.txt b/doc/dial.txt index e984a2c..5088ad5 100644 --- a/doc/dial.txt +++ b/doc/dial.txt @@ -289,6 +289,7 @@ for a particular filetype. }, markdown = { augend.integer.alias.decimal, + augend.ordinal.alias.en, augend.misc.alias.markdown_header, } } @@ -337,6 +338,11 @@ The following aliases are provided by default (See |dial-augends| for detail): `augend.paren.alias.lua_str_literal` `augend.paren.alias.rust_str_literal` + ordinal: + `augend.ordinal.alias.en` + `augend.ordinal.alias.en_neg` + `augend.ordinal.alias.en_old` + misc: `augend.misc.alias.markdown_header` diff --git a/lua/dial/augend.lua b/lua/dial/augend.lua index 699270c..987f5a4 100644 --- a/lua/dial/augend.lua +++ b/lua/dial/augend.lua @@ -4,6 +4,7 @@ local date = require "dial.augend.date" local decimal_fraction = require "dial.augend.decimal_fraction" local hexcolor = require "dial.augend.hexcolor" local integer = require "dial.augend.integer" +local ordinal = require "dial.augend.ordinal" local semver = require "dial.augend.semver" local user = require "dial.augend.user" local paren = require "dial.augend.paren" @@ -16,6 +17,7 @@ return { decimal_fraction = decimal_fraction, hexcolor = hexcolor, integer = integer, + ordinal = ordinal, semver = semver, user = user, paren = paren, diff --git a/lua/dial/augend/common.lua b/lua/dial/augend/common.lua index 37d5fd8..0173c70 100644 --- a/lua/dial/augend/common.lua +++ b/lua/dial/augend/common.lua @@ -7,8 +7,9 @@ local M = {} ---augend の find field を簡単に実装する。 ---@param ptn string ---@param allow_match_before_cursor? boolean +---@param check_query? string ---@return findf -function M.find_pattern(ptn, allow_match_before_cursor) +function M.find_pattern(ptn, allow_match_before_cursor, check_query) ---@param line string ---@param cursor? integer ---@return textrange? @@ -17,7 +18,22 @@ function M.find_pattern(ptn, allow_match_before_cursor) local idx_start = 1 while idx_start <= #line do local s, e = line:find(ptn, idx_start) - if s then + if not s then + -- 検索結果がなければそこで終了 + break + end + local check_e + if check_query then + _, check_e = line:find(check_query, idx_start) + end + if check_e then + if (cursor == nil or cursor <= e) and check_e == e then + return { from = s, to = e } + else + match_before_cursor = { from = s, to = e } + idx_start = e + 1 + end + else -- 検索結果があったら if cursor == nil or cursor <= e then -- cursor が終了文字より後ろにあったら終了 @@ -27,9 +43,6 @@ function M.find_pattern(ptn, allow_match_before_cursor) -- 終了文字の後ろから探し始める idx_start = e + 1 end - else - -- 検索結果がなければそこで終了 - break end end if allow_match_before_cursor then @@ -43,8 +56,9 @@ end -- augend の find field を簡単に実装する。 ---@param ptn string ---@param allow_match_before_cursor? boolean +---@param check_query? string ---@return findf -function M.find_pattern_regex(ptn, allow_match_before_cursor) +function M.find_pattern_regex(ptn, allow_match_before_cursor, check_query) ---@param line string ---@param cursor? integer ---@return textrange? @@ -53,11 +67,25 @@ function M.find_pattern_regex(ptn, allow_match_before_cursor) local idx_start = 1 while idx_start <= #line do local s, e = vim.regex(ptn):match_str(line:sub(idx_start)) - - if s then - s = s + idx_start -- 上で得られた s は相対位置なので - e = e + idx_start - 1 -- 上で得られた s は相対位置なので - + if not s then + -- 検索結果がなければそこで終了 + break + end + local check_e + if check_query then + _, check_e = vim.regex(check_query):match_str(line:sub(idx_start)) + end + s = s + idx_start -- 上で得られた s は相対位置なので + e = e + idx_start - 1 -- 上で得られた s は相対位置なので + if check_e then + check_e = check_e + idx_start - 1 + if (cursor == nil or cursor <= e) and check_e == e then + return { from = s, to = e } + else + match_before_cursor = { from = s, to = e } + idx_start = e + 1 + end + else -- 検索結果があったら if cursor == nil or cursor <= e then -- cursor が終了文字より後ろにあったら終了 @@ -67,9 +95,6 @@ function M.find_pattern_regex(ptn, allow_match_before_cursor) -- 終了文字の後ろから探し始める idx_start = e + 1 end - else - -- 検索結果がなければそこで終了 - break end end if allow_match_before_cursor then diff --git a/lua/dial/augend/ordinal.lua b/lua/dial/augend/ordinal.lua new file mode 100644 index 0000000..7459de7 --- /dev/null +++ b/lua/dial/augend/ordinal.lua @@ -0,0 +1,100 @@ +local common = require "dial.augend.common" +local util = require "dial.util" + +---@alias ordinalSuffix { default: string, special?: string[] } + +---@alias AugendOrdinalConfig { natural?: boolean, suffix?: ordinalSuffix } + +---@class AugendOrdinal: Augend +---@field natural boolean +---@field suffix ordinalSuffix +---@field query string +---@field check_query? string +local AugendOrdinal = {} + +---@param line string +---@param cursor? integer +---@return textrange? +function AugendOrdinal:find(line, cursor) + return common.find_pattern_regex(self.query, false, self.check_query)(line, cursor) +end + +---@param text string +---@param addend integer +---@param cursor? integer +---@return addresult +function AugendOrdinal:add(text, addend, cursor) + local ordinal_query = "%d+" + + if not self.natural then + ordinal_query = "-?" .. ordinal_query + end + + for ordinal in text:gmatch(ordinal_query) do + local cardinal = ordinal + addend + + if (cardinal < 0) and self.natural then + cardinal = 0 + end + + local remainder = math.abs(cardinal) % 100 + + -- WARN: the following statement only works for the english language + local suffix = not vim.tbl_contains({ 11, 12, 13 }, remainder) and self.suffix.special[remainder % 10] + or self.suffix.default + + text = cardinal .. suffix + end + + cursor = 1 + + return { text = text, cursor = cursor } +end + +local M = {} + +---@param config AugendOrdinalConfig +---@return Augend +function M.new(config) + vim.validate("natural", config.natural, "boolean", true) + vim.validate("suffix", config.suffix, "table", true) + + local natural = util.unwrap_or(config.natural, true) + + local suffix = util.unwrap_or(config.suffix, { + default = "th", + special = { + "st", + "nd", + "rd", + }, + }) + + -- WARN: the following queries only work for the english language + local query = ([[\V%s\d\+\a\{1,2}]]):format(util.if_expr(natural, "", [[-\?]])) + local check_query = ([[\V%s\d\+\a\+]]):format(util.if_expr(natural, "", [[-\?]])) + + return setmetatable({ + natural = natural, + suffix = suffix, + query = query, + check_query = check_query, + }, { __index = AugendOrdinal }) +end + +M.alias = { + en = M.new {}, + en_neg = M.new { natural = false }, + en_old = M.new { + suffix = { + default = "th", + special = { + "st", + "d", + "d", + }, + }, + }, +} + +return M diff --git a/tests/dial/augend/ordinal_spec.lua b/tests/dial/augend/ordinal_spec.lua new file mode 100644 index 0000000..6407b77 --- /dev/null +++ b/tests/dial/augend/ordinal_spec.lua @@ -0,0 +1,328 @@ +local ordinal = require("dial.augend").ordinal + +describe("Test of ordinal.alias.en:", function() + local augend = ordinal.alias.en + + describe("find function", function() + it("can find ordinals", function() + assert.are.same(augend:find("1st", 1), { from = 1, to = 3 }) + assert.are.same(augend:find("2nd", 1), { from = 1, to = 3 }) + assert.are.same(augend:find("3rd", 1), { from = 1, to = 3 }) + assert.are.same(augend:find("4th", 1), { from = 1, to = 3 }) + assert.are.same(augend:find("0th", 1), { from = 1, to = 3 }) + + assert.are.same(augend:find("10th", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("11th", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("12th", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("13th", 1), { from = 1, to = 4 }) + + assert.are.same(augend:find("21st", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("22nd", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("23rd", 1), { from = 1, to = 4 }) + + assert.are.same(augend:find("100th", 1), { from = 1, to = 5 }) + assert.are.same(augend:find("1000th", 1), { from = 1, to = 6 }) + + assert.are.same(augend:find("-1st", 1), { from = 2, to = 4 }) + assert.are.same(augend:find("-2nd", 1), { from = 2, to = 4 }) + assert.are.same(augend:find("-3rd", 1), { from = 2, to = 4 }) + assert.are.same(augend:find("-4th", 1), { from = 2, to = 4 }) + + assert.are.same(augend:find("-10th", 1), { from = 2, to = 5 }) + assert.are.same(augend:find("-11th", 1), { from = 2, to = 5 }) + assert.are.same(augend:find("-12th", 1), { from = 2, to = 5 }) + assert.are.same(augend:find("-13th", 1), { from = 2, to = 5 }) + + assert.are.same(augend:find("-21st", 1), { from = 2, to = 5 }) + assert.are.same(augend:find("-22nd", 1), { from = 2, to = 5 }) + assert.are.same(augend:find("-23rd", 1), { from = 2, to = 5 }) + + assert.are.same(augend:find("-100th", 1), { from = 2, to = 6 }) + assert.are.same(augend:find("-1000th", 1), { from = 2, to = 7 }) + + assert.are.same(augend:find("1001st", 4), { from = 1, to = 6 }) + assert.are.same(augend:find("test the 2nd", 1), { from = 10, to = 12 }) + assert.are.same(augend:find("1st 2nd 3rd", 9), { from = 9, to = 11 }) + + assert.are.same(augend:find("manifest-2nd.txt", 1), { from = 10, to = 12 }) + assert.are.same(augend:find("---1st LDoc comment", 1), { from = 4, to = 6 }) + end) + it("ignores non-ordinal elements", function() + assert.are.same(augend:find("1standard", 1), nil) + assert.are.same(augend:find("3rdev", 1), nil) + assert.are.same(augend:find("10thousand", 1), nil) + + assert.are.same(augend:find("5thousand 2nd", 1), { from = 11, to = 13 }) + end) + end) + + describe("add function", function() + it("can increment ordinal", function() + assert.are.same(augend:add("1st", 1, 1), { text = "2nd", cursor = 1 }) + assert.are.same(augend:add("1st", 2, 1), { text = "3rd", cursor = 1 }) + assert.are.same(augend:add("1st", 3, 1), { text = "4th", cursor = 1 }) + assert.are.same(augend:add("1st", 9, 1), { text = "10th", cursor = 1 }) + + assert.are.same(augend:add("2nd", 1, 1), { text = "3rd", cursor = 1 }) + assert.are.same(augend:add("3rd", 1, 1), { text = "4th", cursor = 1 }) + assert.are.same(augend:add("9th", 1, 1), { text = "10th", cursor = 1 }) + + assert.are.same(augend:add("10th", 1, 1), { text = "11th", cursor = 1 }) + assert.are.same(augend:add("11th", 1, 1), { text = "12th", cursor = 1 }) + assert.are.same(augend:add("12th", 1, 1), { text = "13th", cursor = 1 }) + + assert.are.same(augend:add("20th", 1, 1), { text = "21st", cursor = 1 }) + assert.are.same(augend:add("21st", 1, 1), { text = "22nd", cursor = 1 }) + assert.are.same(augend:add("22nd", 1, 1), { text = "23rd", cursor = 1 }) + + assert.are.same(augend:add("99th", 1, 1), { text = "100th", cursor = 1 }) + assert.are.same(augend:add("999th", 1, 1), { text = "1000th", cursor = 1 }) + + assert.are.same(augend:add("1000th", -1, 1), { text = "999th", cursor = 1 }) + assert.are.same(augend:add("100th", -1, 1), { text = "99th", cursor = 1 }) + + assert.are.same(augend:add("24th", -1, 1), { text = "23rd", cursor = 1 }) + assert.are.same(augend:add("23rd", -1, 1), { text = "22nd", cursor = 1 }) + assert.are.same(augend:add("22nd", -1, 1), { text = "21st", cursor = 1 }) + assert.are.same(augend:add("21st", -1, 1), { text = "20th", cursor = 1 }) + + assert.are.same(augend:add("14th", -1, 1), { text = "13th", cursor = 1 }) + assert.are.same(augend:add("13th", -1, 1), { text = "12th", cursor = 1 }) + assert.are.same(augend:add("12th", -1, 1), { text = "11th", cursor = 1 }) + assert.are.same(augend:add("11th", -1, 1), { text = "10th", cursor = 1 }) + + assert.are.same(augend:add("10th", -1, 1), { text = "9th", cursor = 1 }) + assert.are.same(augend:add("4th", -1, 1), { text = "3rd", cursor = 1 }) + assert.are.same(augend:add("3rd", -1, 1), { text = "2nd", cursor = 1 }) + assert.are.same(augend:add("2nd", -1, 1), { text = "1st", cursor = 1 }) + + assert.are.same(augend:add("1st", -1, 1), { text = "0th", cursor = 1 }) + assert.are.same(augend:add("1st", -2, 1), { text = "0th", cursor = 1 }) + + assert.are.same(augend:add("0th", -1, 1), { text = "0th", cursor = 1 }) + end) + end) +end) + +describe("Test of ordinal.alias.en_neg:", function() + local augend = ordinal.alias.en_neg + + describe("find function", function() + it("can find ordinals", function() + assert.are.same(augend:find("1st", 1), { from = 1, to = 3 }) + assert.are.same(augend:find("2nd", 1), { from = 1, to = 3 }) + assert.are.same(augend:find("3rd", 1), { from = 1, to = 3 }) + assert.are.same(augend:find("4th", 1), { from = 1, to = 3 }) + assert.are.same(augend:find("0th", 1), { from = 1, to = 3 }) + + assert.are.same(augend:find("10th", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("11th", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("12th", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("13th", 1), { from = 1, to = 4 }) + + assert.are.same(augend:find("21st", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("22nd", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("23rd", 1), { from = 1, to = 4 }) + + assert.are.same(augend:find("100th", 1), { from = 1, to = 5 }) + assert.are.same(augend:find("1000th", 1), { from = 1, to = 6 }) + + assert.are.same(augend:find("-1st", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("-2nd", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("-3rd", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("-4th", 1), { from = 1, to = 4 }) + + assert.are.same(augend:find("-10th", 1), { from = 1, to = 5 }) + assert.are.same(augend:find("-11th", 1), { from = 1, to = 5 }) + assert.are.same(augend:find("-12th", 1), { from = 1, to = 5 }) + assert.are.same(augend:find("-13th", 1), { from = 1, to = 5 }) + + assert.are.same(augend:find("-21st", 1), { from = 1, to = 5 }) + assert.are.same(augend:find("-22nd", 1), { from = 1, to = 5 }) + assert.are.same(augend:find("-23rd", 1), { from = 1, to = 5 }) + + assert.are.same(augend:find("-100th", 1), { from = 1, to = 6 }) + assert.are.same(augend:find("-1000th", 1), { from = 1, to = 7 }) + + assert.are.same(augend:find("1001st", 4), { from = 1, to = 6 }) + assert.are.same(augend:find("test the 2nd", 1), { from = 10, to = 12 }) + assert.are.same(augend:find("1st 2nd 3rd", 9), { from = 9, to = 11 }) + end) + it("ignores non-ordinal elements", function() + assert.are.same(augend:find("1standard", 1), nil) + assert.are.same(augend:find("3rdev", 1), nil) + assert.are.same(augend:find("10thousand", 1), nil) + + assert.are.same(augend:find("5thousand 2nd", 1), { from = 11, to = 13 }) + end) + end) + + describe("add function", function() + it("can increment ordinal", function() + assert.are.same(augend:add("1st", 1, 1), { text = "2nd", cursor = 1 }) + assert.are.same(augend:add("1st", 2, 1), { text = "3rd", cursor = 1 }) + assert.are.same(augend:add("1st", 3, 1), { text = "4th", cursor = 1 }) + assert.are.same(augend:add("1st", 9, 1), { text = "10th", cursor = 1 }) + + assert.are.same(augend:add("2nd", 1, 1), { text = "3rd", cursor = 1 }) + assert.are.same(augend:add("3rd", 1, 1), { text = "4th", cursor = 1 }) + assert.are.same(augend:add("9th", 1, 1), { text = "10th", cursor = 1 }) + + assert.are.same(augend:add("10th", 1, 1), { text = "11th", cursor = 1 }) + assert.are.same(augend:add("11th", 1, 1), { text = "12th", cursor = 1 }) + assert.are.same(augend:add("12th", 1, 1), { text = "13th", cursor = 1 }) + + assert.are.same(augend:add("20th", 1, 1), { text = "21st", cursor = 1 }) + assert.are.same(augend:add("21st", 1, 1), { text = "22nd", cursor = 1 }) + assert.are.same(augend:add("22nd", 1, 1), { text = "23rd", cursor = 1 }) + + assert.are.same(augend:add("99th", 1, 1), { text = "100th", cursor = 1 }) + assert.are.same(augend:add("999th", 1, 1), { text = "1000th", cursor = 1 }) + + assert.are.same(augend:add("1000th", -1, 1), { text = "999th", cursor = 1 }) + assert.are.same(augend:add("100th", -1, 1), { text = "99th", cursor = 1 }) + + assert.are.same(augend:add("24th", -1, 1), { text = "23rd", cursor = 1 }) + assert.are.same(augend:add("23rd", -1, 1), { text = "22nd", cursor = 1 }) + assert.are.same(augend:add("22nd", -1, 1), { text = "21st", cursor = 1 }) + assert.are.same(augend:add("21st", -1, 1), { text = "20th", cursor = 1 }) + + assert.are.same(augend:add("14th", -1, 1), { text = "13th", cursor = 1 }) + assert.are.same(augend:add("13th", -1, 1), { text = "12th", cursor = 1 }) + assert.are.same(augend:add("12th", -1, 1), { text = "11th", cursor = 1 }) + assert.are.same(augend:add("11th", -1, 1), { text = "10th", cursor = 1 }) + + assert.are.same(augend:add("10th", -1, 1), { text = "9th", cursor = 1 }) + assert.are.same(augend:add("4th", -1, 1), { text = "3rd", cursor = 1 }) + assert.are.same(augend:add("3rd", -1, 1), { text = "2nd", cursor = 1 }) + assert.are.same(augend:add("2nd", -1, 1), { text = "1st", cursor = 1 }) + + assert.are.same(augend:add("1st", -1, 1), { text = "0th", cursor = 1 }) + assert.are.same(augend:add("1st", -2, 1), { text = "-1st", cursor = 1 }) + assert.are.same(augend:add("1st", -3, 1), { text = "-2nd", cursor = 1 }) + assert.are.same(augend:add("1st", -4, 1), { text = "-3rd", cursor = 1 }) + assert.are.same(augend:add("1st", -5, 1), { text = "-4th", cursor = 1 }) + + assert.are.same(augend:add("0th", -1, 1), { text = "-1st", cursor = 1 }) + assert.are.same(augend:add("-1st", -1, 1), { text = "-2nd", cursor = 1 }) + assert.are.same(augend:add("-2nd", -1, 1), { text = "-3rd", cursor = 1 }) + assert.are.same(augend:add("-3rd", -1, 1), { text = "-4th", cursor = 1 }) + assert.are.same(augend:add("-9th", -1, 1), { text = "-10th", cursor = 1 }) + + assert.are.same(augend:add("-10th", -1, 1), { text = "-11th", cursor = 1 }) + assert.are.same(augend:add("-11th", -1, 1), { text = "-12th", cursor = 1 }) + assert.are.same(augend:add("-12th", -1, 1), { text = "-13th", cursor = 1 }) + + assert.are.same(augend:add("-20th", -1, 1), { text = "-21st", cursor = 1 }) + assert.are.same(augend:add("-21st", -1, 1), { text = "-22nd", cursor = 1 }) + assert.are.same(augend:add("-22nd", -1, 1), { text = "-23rd", cursor = 1 }) + + assert.are.same(augend:add("-99th", -1, 1), { text = "-100th", cursor = 1 }) + assert.are.same(augend:add("-999th", -1, 1), { text = "-1000th", cursor = 1 }) + end) + end) +end) + +describe("Test of ordinal.alias.en_old:", function() + local augend = ordinal.alias.en_old + + describe("find function", function() + it("can find ordinals", function() + assert.are.same(augend:find("1st", 1), { from = 1, to = 3 }) + assert.are.same(augend:find("2d", 1), { from = 1, to = 2 }) + assert.are.same(augend:find("3d", 1), { from = 1, to = 2 }) + assert.are.same(augend:find("4th", 1), { from = 1, to = 3 }) + assert.are.same(augend:find("0th", 1), { from = 1, to = 3 }) + + assert.are.same(augend:find("10th", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("11th", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("12th", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("13th", 1), { from = 1, to = 4 }) + + assert.are.same(augend:find("21st", 1), { from = 1, to = 4 }) + assert.are.same(augend:find("22d", 1), { from = 1, to = 3 }) + assert.are.same(augend:find("23d", 1), { from = 1, to = 3 }) + + assert.are.same(augend:find("100th", 1), { from = 1, to = 5 }) + assert.are.same(augend:find("1000th", 1), { from = 1, to = 6 }) + + assert.are.same(augend:find("-1st", 1), { from = 2, to = 4 }) + assert.are.same(augend:find("-2d", 1), { from = 2, to = 3 }) + assert.are.same(augend:find("-3d", 1), { from = 2, to = 3 }) + assert.are.same(augend:find("-4th", 1), { from = 2, to = 4 }) + + assert.are.same(augend:find("-10th", 1), { from = 2, to = 5 }) + assert.are.same(augend:find("-11th", 1), { from = 2, to = 5 }) + assert.are.same(augend:find("-12th", 1), { from = 2, to = 5 }) + assert.are.same(augend:find("-13th", 1), { from = 2, to = 5 }) + + assert.are.same(augend:find("-21st", 1), { from = 2, to = 5 }) + assert.are.same(augend:find("-22d", 1), { from = 2, to = 4 }) + assert.are.same(augend:find("-23d", 1), { from = 2, to = 4 }) + + assert.are.same(augend:find("-100th", 1), { from = 2, to = 6 }) + assert.are.same(augend:find("-1000th", 1), { from = 2, to = 7 }) + + assert.are.same(augend:find("1001st", 4), { from = 1, to = 6 }) + assert.are.same(augend:find("test the 2d", 1), { from = 10, to = 11 }) + assert.are.same(augend:find("1st 2d 3d", 9), { from = 8, to = 9 }) + + assert.are.same(augend:find("manifest-2d.txt", 1), { from = 10, to = 11 }) + assert.are.same(augend:find("---1st LDoc comment", 1), { from = 4, to = 6 }) + end) + it("ignores non-ordinal elements", function() + assert.are.same(augend:find("1standard", 1), nil) + assert.are.same(augend:find("3dev", 1), nil) + assert.are.same(augend:find("10thousand", 1), nil) + + assert.are.same(augend:find("5thousand 2d", 1), { from = 11, to = 12 }) + end) + end) + + describe("add function", function() + it("can increment ordinal", function() + assert.are.same(augend:add("1st", 1, 1), { text = "2d", cursor = 1 }) + assert.are.same(augend:add("1st", 2, 1), { text = "3d", cursor = 1 }) + assert.are.same(augend:add("1st", 3, 1), { text = "4th", cursor = 1 }) + assert.are.same(augend:add("1st", 9, 1), { text = "10th", cursor = 1 }) + + assert.are.same(augend:add("2d", 1, 1), { text = "3d", cursor = 1 }) + assert.are.same(augend:add("3d", 1, 1), { text = "4th", cursor = 1 }) + assert.are.same(augend:add("9th", 1, 1), { text = "10th", cursor = 1 }) + + assert.are.same(augend:add("10th", 1, 1), { text = "11th", cursor = 1 }) + assert.are.same(augend:add("11th", 1, 1), { text = "12th", cursor = 1 }) + assert.are.same(augend:add("12th", 1, 1), { text = "13th", cursor = 1 }) + + assert.are.same(augend:add("20th", 1, 1), { text = "21st", cursor = 1 }) + assert.are.same(augend:add("21st", 1, 1), { text = "22d", cursor = 1 }) + assert.are.same(augend:add("22d", 1, 1), { text = "23d", cursor = 1 }) + + assert.are.same(augend:add("99th", 1, 1), { text = "100th", cursor = 1 }) + assert.are.same(augend:add("999th", 1, 1), { text = "1000th", cursor = 1 }) + + assert.are.same(augend:add("1000th", -1, 1), { text = "999th", cursor = 1 }) + assert.are.same(augend:add("100th", -1, 1), { text = "99th", cursor = 1 }) + + assert.are.same(augend:add("24th", -1, 1), { text = "23d", cursor = 1 }) + assert.are.same(augend:add("23d", -1, 1), { text = "22d", cursor = 1 }) + assert.are.same(augend:add("22d", -1, 1), { text = "21st", cursor = 1 }) + assert.are.same(augend:add("21st", -1, 1), { text = "20th", cursor = 1 }) + + assert.are.same(augend:add("14th", -1, 1), { text = "13th", cursor = 1 }) + assert.are.same(augend:add("13th", -1, 1), { text = "12th", cursor = 1 }) + assert.are.same(augend:add("12th", -1, 1), { text = "11th", cursor = 1 }) + assert.are.same(augend:add("11th", -1, 1), { text = "10th", cursor = 1 }) + + assert.are.same(augend:add("10th", -1, 1), { text = "9th", cursor = 1 }) + assert.are.same(augend:add("4th", -1, 1), { text = "3d", cursor = 1 }) + assert.are.same(augend:add("3d", -1, 1), { text = "2d", cursor = 1 }) + assert.are.same(augend:add("2d", -1, 1), { text = "1st", cursor = 1 }) + + assert.are.same(augend:add("1st", -1, 1), { text = "0th", cursor = 1 }) + assert.are.same(augend:add("1st", -2, 1), { text = "0th", cursor = 1 }) + + assert.are.same(augend:add("0th", -1, 1), { text = "0th", cursor = 1 }) + end) + end) +end)