summaryrefslogtreecommitdiff
path: root/lua/clever_f/direct_preview_planner.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua/clever_f/direct_preview_planner.lua')
-rw-r--r--lua/clever_f/direct_preview_planner.lua250
1 files changed, 0 insertions, 250 deletions
diff --git a/lua/clever_f/direct_preview_planner.lua b/lua/clever_f/direct_preview_planner.lua
deleted file mode 100644
index 6735924..0000000
--- a/lua/clever_f/direct_preview_planner.lua
+++ /dev/null
@@ -1,250 +0,0 @@
-local case_policy = require("clever_f.case_policy")
-local domain = require("clever_f.domain")
-local text_topology = require("clever_f.text_topology")
-
-local M = {}
-local DirectPreviewPlanner = {}
-DirectPreviewPlanner.__index = DirectPreviewPlanner
-M.DirectPreviewPlanner = DirectPreviewPlanner
-
-local planner_records = setmetatable({}, { __mode = "k" })
-
-local function fail(message, level)
- error(message, (level or 1) + 1)
-end
-
-local function require_view(view)
- if not text_topology.TextView.is(view) then
- fail("direct preview text must be a TextView", 3)
- end
- return view
-end
-
-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] = {
- case_resolver = require_case_resolver(options),
- }
- return planner
-end
-
-function DirectPreviewPlanner.is(value)
- return type(value) == "table" and planner_records[value] ~= nil
-end
-
-function DirectPreviewPlanner:scan_current_line(view, origin, direction)
- require_view(view)
- origin = domain.Position.coerce(origin)
- direction = domain.Direction.from_string(direction)
- local records = {}
- for position, character, span in view:iter_strict(
- origin,
- direction,
- domain.SearchScope.CURRENT_LINE
- ) do
- records[#records + 1] = {
- position = position,
- character = character,
- span = span,
- }
- end
- return records
-end
-
-function M.marker_position(descriptor, target_position)
- domain.Descriptor.from_string(descriptor)
- return domain.Position.coerce(target_position)
-end
-
-function M.normalize_count(count)
- return domain.Count.to_number(count)
-end
-
-function DirectPreviewPlanner:normalize_count(count)
- return M.normalize_count(count)
-end
-
-function M.direction_for_descriptor(descriptor)
- descriptor = domain.Descriptor.from_string(descriptor)
- if domain.Descriptor.is_uppercase(descriptor) then
- return domain.Direction.BACKWARD
- end
- return domain.Direction.FORWARD
-end
-
-function DirectPreviewPlanner:scan_for_descriptor(view, origin, descriptor)
- return self:scan_current_line(
- view,
- origin,
- M.direction_for_descriptor(descriptor)
- )
-end
-
-local function increment_counter(counters, character)
- local value = (counters[character] or 0) + 1
- counters[character] = value
- return value
-end
-
-local function is_upper_ascii(character)
- if type(character) ~= "string" or #character ~= 1 then
- return false
- end
- local code = character:byte(1)
- return code >= string.byte("A") and code <= string.byte("Z")
-end
-
-local function increment_smart_counters(counters, character)
- local exact_count = increment_counter(counters, character)
- local lowercase_count
- if is_upper_ascii(character) then
- local lowercase = string.char(character:byte(1) + 32)
- lowercase_count = increment_counter(counters, lowercase)
- end
- return exact_count, lowercase_count
-end
-
-M.is_upper_ascii = is_upper_ascii
-
-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 M.case_grouping_settings(settings)
- settings = settings or {}
- if type(settings) ~= "table" then
- fail("direct preview case settings must be a table", 2)
- end
- return {
- ignore_case = require_case_setting(settings, "ignore_case"),
- smart_case = require_case_setting(settings, "smart_case"),
- }
-end
-
-function M.validate_marker_positions(view, positions)
- require_view(view)
- if type(positions) ~= "table" then
- fail("direct preview markers must be a list of positions", 2)
- end
-
- local result = {}
- local seen = {}
- local item_count = 0
- for key, position in pairs(positions) do
- if type(key) ~= "number"
- or key ~= math.floor(key)
- or key < 1
- or key > #positions
- then
- fail("direct preview markers must be a list of positions", 2)
- end
- position = domain.Position.coerce(position)
- if not view:is_character_start(position) then
- fail("direct preview marker must start an editor character", 2)
- end
- local identity = tostring(position.line) .. ":" .. tostring(position.byte_column)
- if seen[identity] then
- fail("direct preview marker positions must be unique", 2)
- end
- seen[identity] = true
- result[key] = position
- item_count = item_count + 1
- end
- if item_count ~= #positions then
- fail("direct preview markers must be a list of positions", 2)
- end
- return result
-end
-
-function DirectPreviewPlanner:plan(view, origin, descriptor, count, settings)
- local grouping = M.case_grouping_settings(settings)
- local ignore_case = grouping.ignore_case
- local smart_case = grouping.smart_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
- local selected
- if ignore_case then
- selected = increment_counter(
- counters,
- resolver:lowercase(record.character)
- ) == selected_occurrence
- elseif smart_case then
- local exact_count, lowercase_count = increment_smart_counters(
- counters,
- record.character
- )
- selected = exact_count == selected_occurrence
- or lowercase_count == selected_occurrence
- else
- selected = increment_counter(
- counters,
- record.character
- ) == selected_occurrence
- end
- if selected then
- positions[#positions + 1] = M.marker_position(descriptor, record.position)
- end
- end
- return M.validate_marker_positions(view, positions)
-end
-
-function M.new(options)
- return DirectPreviewPlanner.new(options)
-end
-
-function M.scan_current_line(view, origin, direction)
- return DirectPreviewPlanner.new():scan_current_line(view, origin, direction)
-end
-
-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, settings, options)
- return DirectPreviewPlanner.new(options):plan(
- view,
- origin,
- descriptor,
- count,
- settings
- )
-end
-
-setmetatable(M, {
- __call = function(_, options)
- return DirectPreviewPlanner.new(options)
- end,
-})
-
-return M