diff options
| -rw-r--r-- | lua/clever_f/direct_preview_planner.lua | 72 | ||||
| -rw-r--r-- | tests/run.lua | 27 |
2 files changed, 88 insertions, 11 deletions
diff --git a/lua/clever_f/direct_preview_planner.lua b/lua/clever_f/direct_preview_planner.lua index 9a6b0d2..51d7b18 100644 --- a/lua/clever_f/direct_preview_planner.lua +++ b/lua/clever_f/direct_preview_planner.lua @@ -1,3 +1,4 @@ +local case_policy = require("clever_f.case_policy") local domain = require("clever_f.domain") local text_topology = require("clever_f.text_topology") @@ -19,14 +20,37 @@ local function require_view(view) return view end -function DirectPreviewPlanner.new() +local function require_case_resolver(options) + options = options or {} + if type(options) == "function" then + options = { lowercase = options } + end + if type(options) ~= "table" then + fail("DirectPreviewPlanner options must be a table", 3) + end + local resolver = options.case_resolver + if resolver == nil then + resolver = case_policy.new({ lowercase = options.lowercase }) + end + if type(resolver) ~= "table" or type(resolver.lowercase) ~= "function" then + fail("DirectPreviewPlanner case resolver must provide lowercase", 3) + end + return resolver +end + +function DirectPreviewPlanner.new(options) + if DirectPreviewPlanner.is(options) then + return options + end local planner = setmetatable({}, DirectPreviewPlanner) - planner_records[planner] = true + planner_records[planner] = { + case_resolver = require_case_resolver(options), + } return planner end function DirectPreviewPlanner.is(value) - return type(value) == "table" and planner_records[value] == true + return type(value) == "table" and planner_records[value] ~= nil end function DirectPreviewPlanner:scan_current_line(view, origin, direction) @@ -78,20 +102,40 @@ local function increment_counter(counters, character) return value end -function DirectPreviewPlanner:plan(view, origin, descriptor, count) +local function require_case_setting(settings, name) + local value = settings[name] + if value == nil then + return false + end + if type(value) ~= "boolean" then + fail("direct preview " .. name .. " must be a Boolean", 3) + end + return value +end + +function DirectPreviewPlanner:plan(view, origin, descriptor, count, settings) + settings = settings or {} + if type(settings) ~= "table" then + fail("direct preview case settings must be a table", 2) + end + local ignore_case = require_case_setting(settings, "ignore_case") local selected_occurrence = self:normalize_count(count) local counters = {} local positions = {} + local resolver = planner_records[self].case_resolver for _, record in ipairs(self:scan_for_descriptor(view, origin, descriptor)) do - if increment_counter(counters, record.character) == selected_occurrence then + local counter_key = ignore_case + and resolver:lowercase(record.character) + or record.character + if increment_counter(counters, counter_key) == selected_occurrence then positions[#positions + 1] = record.position end end return positions end -function M.new() - return DirectPreviewPlanner.new() +function M.new(options) + return DirectPreviewPlanner.new(options) end function M.scan_current_line(view, origin, direction) @@ -102,13 +146,19 @@ function M.scan_for_descriptor(view, origin, descriptor) return DirectPreviewPlanner.new():scan_for_descriptor(view, origin, descriptor) end -function M.plan(view, origin, descriptor, count) - return DirectPreviewPlanner.new():plan(view, origin, descriptor, count) +function M.plan(view, origin, descriptor, count, settings, options) + return DirectPreviewPlanner.new(options):plan( + view, + origin, + descriptor, + count, + settings + ) end setmetatable(M, { - __call = function() - return DirectPreviewPlanner.new() + __call = function(_, options) + return DirectPreviewPlanner.new(options) end, }) diff --git a/tests/run.lua b/tests/run.lua index 7ec0baf..3c2a077 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -4649,6 +4649,33 @@ test("Default direct previews count each exact character", function() list_same({ 15 }, columns(planner:plan(view, origin, "f", 3))) end) +test("Ignore-case previews count editor-folded character classes", function() + local folded = {} + local planner = direct_preview_planner.new({ + lowercase = function(character) + folded[#folded + 1] = character + return string.lower(character) + end, + }) + local view = text_topology.new({ "xAaBbC" }, "utf-8") + local origin = domain.Position.new(1, 1) + + local positions = planner:plan(view, origin, "f", 2, { + ignore_case = true, + smart_case = false, + }) + same(2, #positions) + same(domain.Position.new(1, 3), positions[1]) + same(domain.Position.new(1, 5), positions[2]) + list_same({ "A", "a", "B", "b", "C" }, folded) + + local exact = planner:plan(view, origin, "f", 2, { + ignore_case = false, + smart_case = false, + }) + same(0, #exact) +end) + for _, item in ipairs(tests) do local ok, failure = xpcall(item.body, debug.traceback) if not ok then |
