local fs = require "luacheck.fs"
|
local utils = require "luacheck.utils"
|
|
-- Only ?, *, ** and simple character classes (with ranges and negation) are supported.
|
-- Hidden files are not treated specially. Special characters can't be escaped.
|
local globbing = {}
|
|
local function is_regular_path(glob)
|
return not glob:find("[*?%[]")
|
end
|
|
local function get_parts(path)
|
local parts = {}
|
|
for part in path:gmatch("[^"..utils.dir_sep.."]+") do
|
table.insert(parts, part)
|
end
|
|
return parts
|
end
|
|
local function glob_pattern_escaper(c)
|
return ((c == "*" or c == "?") and "." or "%")..c
|
end
|
|
local function glob_range_escaper(c)
|
return c == "-" and c or ("%"..c)
|
end
|
|
local function glob_part_to_pattern(glob_part)
|
local buffer = {"^"}
|
local i = 1
|
|
while i <= #glob_part do
|
local bracketless
|
bracketless, i = glob_part:match("([^%[]*)()", i)
|
table.insert(buffer, (bracketless:gsub("%p", glob_pattern_escaper)))
|
|
if glob_part:sub(i, i) == "[" then
|
table.insert(buffer, "[")
|
i = i + 1
|
local first_char = glob_part:sub(i, i)
|
|
if first_char == "!" then
|
table.insert(buffer, "^")
|
i = i + 1
|
elseif first_char == "]" then
|
table.insert(buffer, "%]")
|
i = i + 1
|
end
|
|
bracketless, i = glob_part:match("([^%]]*)()", i)
|
|
if bracketless:sub(1, 1) == "-" then
|
table.insert(buffer, "%-")
|
bracketless = bracketless:sub(2)
|
end
|
|
local last_dash = ""
|
|
if bracketless:sub(-1) == "-" then
|
last_dash = "-"
|
bracketless = bracketless:sub(1, -2)
|
end
|
|
table.insert(buffer, (bracketless:gsub("%p", glob_range_escaper)))
|
table.insert(buffer, last_dash.."]")
|
i = i + 1
|
end
|
end
|
|
table.insert(buffer, "$")
|
return table.concat(buffer)
|
end
|
|
local function part_match(glob_part, path_part)
|
return utils.pmatch(path_part, glob_part_to_pattern(glob_part))
|
end
|
|
local function parts_match(glob_parts, glob_i, path_parts, path_i)
|
local glob_part = glob_parts[glob_i]
|
|
if not glob_part then
|
-- Reached glob end, path matches the glob or its subdirectory.
|
-- E.g. path "foo/bar/baz/src.lua" matches glob "foo/*/baz".
|
return true
|
end
|
|
if glob_part == "**" then
|
-- "**" can consume any number of path parts.
|
for i = path_i, #path_parts + 1 do
|
if parts_match(glob_parts, glob_i + 1, path_parts, i) then
|
return true
|
end
|
end
|
|
return false
|
end
|
|
local path_part = path_parts[path_i]
|
return path_part and part_match(glob_part, path_part) and (
|
parts_match(glob_parts, glob_i + 1, path_parts, path_i + 1))
|
end
|
|
-- Checks if a path matches a globbing pattern.
|
-- Both must be absolute.
|
function globbing.match(glob, path)
|
if is_regular_path(glob) then
|
return fs.is_subpath(glob, path)
|
end
|
|
local glob_base, path_base
|
glob_base, glob = fs.split_base(glob)
|
path_base, path = fs.split_base(path)
|
|
if glob_base ~= path_base then
|
return false
|
end
|
|
local glob_parts = get_parts(glob)
|
local path_parts = get_parts(path)
|
return parts_match(glob_parts, 1, path_parts, 1)
|
end
|
|
-- Checks if glob1 is less specific than glob2 and should be applied
|
-- first in overrides.
|
function globbing.compare(glob1, glob2)
|
local base1, base2
|
base1, glob1 = fs.split_base(glob1)
|
base2, glob2 = fs.split_base(glob2)
|
|
if base1 ~= base2 then
|
return base1 < base2
|
end
|
|
local parts1 = get_parts(glob1)
|
local parts2 = get_parts(glob2)
|
|
for i = 1, math.max(#parts1, #parts2) do
|
if not parts1[i] then
|
return true
|
elseif not parts2[i] then
|
return false
|
end
|
|
if (parts1[i] == "**" or parts2[i] == "**") and parts1[i] ~= parts2[i] then
|
return parts1[i] == "**"
|
end
|
|
local _, specials1 = parts1[i]:gsub("[%*%?%[]", {})
|
local _, specials2 = parts2[i]:gsub("[%*%?%[]", {})
|
|
if specials1 ~= specials2 then
|
return specials1 > specials2
|
end
|
end
|
|
return glob1 < glob2
|
end
|
|
return globbing
|