1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
local domain = require("clever_tee.domain")
local M = {}
local CasePolicyResolver = {}
M.CasePolicyResolver = CasePolicyResolver
local resolver_records = setmetatable({}, { __mode = "k" })
local function fail(message, level)
error(message, (level or 1) + 1)
end
local function require_target(target)
if not domain.TargetValue.is(target) then
fail("case policy target must be a TargetValue", 2)
end
return target
end
local function require_boolean(value, name)
if type(value) ~= "boolean" then
fail((name or "value") .. " must be a Boolean", 2)
end
return value
end
local function require_string(value, name, allow_empty)
if type(value) ~= "string" or (not allow_empty and value == "") then
fail((name or "value") .. " must be a string", 2)
end
return value
end
local function default_lowercase(value)
local runtime = rawget(_G, "vim")
if type(runtime) ~= "table"
or type(runtime.fn) ~= "table"
or type(runtime.fn.tolower) ~= "function"
then
fail("editor-compatible case conversion requires Nvim or a lowercase converter", 2)
end
return runtime.fn.tolower(value)
end
local function lowercase_function(options)
if options == nil then
return default_lowercase
end
if type(options) == "function" then
return options
end
if type(options) ~= "table" then
fail("case policy options must be a table or lowercase function", 3)
end
local lowercase = options.lowercase or options.to_lower
if lowercase == nil then
return default_lowercase
end
if type(lowercase) ~= "function" then
fail("case policy lowercase converter must be a function", 3)
end
return lowercase
end
local resolver_metatable = {
__index = CasePolicyResolver,
__newindex = function()
fail("CasePolicyResolver values are immutable", 2)
end,
__tostring = function()
return "case-policy-resolver"
end,
__metatable = "clever_tee.case_policy.CasePolicyResolver",
}
function CasePolicyResolver.new(options)
if CasePolicyResolver.is(options) then
return options
end
local resolver = setmetatable({}, resolver_metatable)
resolver_records[resolver] = {
lowercase = lowercase_function(options),
}
return resolver
end
function CasePolicyResolver.is(value)
return type(value) == "table" and resolver_records[value] ~= nil
end
function M.new(options)
return CasePolicyResolver.new(options)
end
setmetatable(M, {
__call = function(_, options)
return CasePolicyResolver.new(options)
end,
})
function M.is_lower_ascii(value)
if domain.TargetValue.is(value) then
value = value.value
end
if type(value) ~= "string" or #value ~= 1 then
return false
end
local byte = value:byte(1)
return byte >= string.byte("a") and byte <= string.byte("z")
end
function M.resolve_case_mode(target, ignore_case, smart_case)
target = require_target(target)
require_boolean(ignore_case, "ignore_case")
require_boolean(smart_case, "smart_case")
if ignore_case then
return domain.CaseMode.INSENSITIVE
end
if smart_case and M.is_lower_ascii(target) then
return domain.CaseMode.INSENSITIVE
end
return domain.CaseMode.SENSITIVE
end
function CasePolicyResolver:resolve(target, ignore_case, smart_case)
if type(ignore_case) == "table" and smart_case == nil then
local match_policy = ignore_case
ignore_case = match_policy.ignore_case
smart_case = match_policy.smart_case
end
return M.resolve_case_mode(target, ignore_case, smart_case)
end
function CasePolicyResolver:lowercase(value)
require_string(value, "case comparison value", true)
local lowercase = resolver_records[self].lowercase(value)
if type(lowercase) ~= "string" then
fail("case policy lowercase converter must return a string", 2)
end
return lowercase
end
function CasePolicyResolver:equal(left, right, case_mode)
require_string(left, "left case comparison value", true)
require_string(right, "right case comparison value", true)
case_mode = domain.CaseMode.from_string(case_mode)
if case_mode == domain.CaseMode.SENSITIVE then
return left == right
end
return self:lowercase(left) == self:lowercase(right)
end
function CasePolicyResolver:comparator(target_character, case_mode)
require_string(target_character, "target character", true)
case_mode = domain.CaseMode.from_string(case_mode)
if target_character == "" then
return function()
return false
end
end
if case_mode == domain.CaseMode.SENSITIVE then
return function(candidate_character)
return type(candidate_character) == "string"
and candidate_character ~= ""
and candidate_character == target_character
end
end
local folded_target = self:lowercase(target_character)
local lowercase = resolver_records[self].lowercase
return function(candidate_character)
if type(candidate_character) ~= "string" or candidate_character == "" then
return false
end
local folded_candidate = lowercase(candidate_character)
if type(folded_candidate) ~= "string" then
fail("case policy lowercase converter must return a string", 2)
end
return folded_candidate == folded_target
end
end
M.resolve = M.resolve_case_mode
M.is_lowercase_ascii = M.is_lower_ascii
return M
|