Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: PR

on:
pull_request:
branches: [ master ]

jobs:
checks:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Lua
uses: leafo/gh-actions-lua@v10
with:
luaVersion: "5.4"

- name: Setup LuaRocks
uses: leafo/gh-actions-luarocks@v4

- name: Install dependencies
run: luarocks install --deps-only gh-co.nvim-0.0.5-1.rockspec

- name: Lint
run: PATH="./lua_modules/bin:$PATH" luacheck lua

- name: Test
run: luarocks test
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/luarocks
/lua_modules
/.luarocks
9 changes: 9 additions & 0 deletions .luacheckrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
std = "luajit"
globals = {"vim"}

ignore = {
-- unused loop variable
"213",
-- ignore unused test functions
"[Tt]est[%w_]+",
}
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
## v0.0.5

- add luarocks & unit tests
- fix incompatibility with CODEOWNERS spec, the plugin can now work with various patterns, can handle paths marked with no owner(s) or wildcard patterns
- fix highlighting, add treesitter support

## v0.0.4

- syntax highlighting for `CODEOWNERS` file

## v0.0.3
Expand Down
33 changes: 33 additions & 0 deletions gh-co.nvim-0.0.4-1.rockspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
rockspec_format = "3.0"
package = "gh-co.nvim"
version = "0.0.4-1"
source = {
url = "git+ssh://git@github.com/comatory/gh-co.nvim.git"
}
description = {
summary = "Github CODEOWNERS Neovim plugin",
detailed = "Displays the code owners for current buffer, all opened buffers or lists owners by providing git SHAs",
homepage = "https://github.com/comatory/gh-co.nvim",
license = "CC0 1.0 Universal"
}
dependencies = {
"lua >= 5.1",
"luacheck",
}
test_dependencies = {
"luaunit >= 3.4"
}
build = {
type = "builtin",
modules = {
["gh-co.co"] = "lua/gh-co/co.lua",
["gh-co.fs"] = "lua/gh-co/fs.lua",
["gh-co.git"] = "lua/gh-co/git.lua",
["gh-co.init"] = "lua/gh-co/init.lua",
["gh-co.syntax"] = "lua/gh-co/syntax.lua"
}
}
test = {
type = "command",
command = "lua -l lua/setup lua/gh-co/co.test.lua -o TAP"
}
33 changes: 33 additions & 0 deletions gh-co.nvim-0.0.5-1.rockspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
rockspec_format = "3.0"
package = "gh-co.nvim"
version = "0.0.5-1"
source = {
url = "git+ssh://git@github.com/comatory/gh-co.nvim.git"
}
description = {
summary = "Github CODEOWNERS Neovim plugin",
detailed = "Displays the code owners for current buffer, all opened buffers or lists owners by providing git SHAs",
homepage = "https://github.com/comatory/gh-co.nvim",
license = "CC0 1.0 Universal"
}
dependencies = {
"lua >= 5.1",
"luacheck",
}
test_dependencies = {
"luaunit >= 3.4"
}
build = {
type = "builtin",
modules = {
["gh-co.co"] = "lua/gh-co/co.lua",
["gh-co.fs"] = "lua/gh-co/fs.lua",
["gh-co.git"] = "lua/gh-co/git.lua",
["gh-co.init"] = "lua/gh-co/init.lua",
["gh-co.syntax"] = "lua/gh-co/syntax.lua"
}
}
test = {
type = "command",
command = "lua -l lua/setup lua/gh-co/co.test.lua -o TAP"
}
55 changes: 47 additions & 8 deletions lua/gh-co/co.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,52 @@ local function isComment(pathPattern)
end

local function buildEscapedPattern(rawPattern)
return string.gsub(rawPattern, "%-", "%%-")
-- Escape Lua pattern special characters except *
local escaped = string.gsub(rawPattern, "([%-%+%?%(%)])", "%%%1")

-- Handle ** first (before single *) - use placeholder to avoid conflicts
escaped = string.gsub(escaped, "%*%*", "__DOUBLESTAR__")

-- Convert remaining * to match any character except /
escaped = string.gsub(escaped, "%*", "[^/]*")

-- Replace placeholder with pattern that matches any path including /
escaped = string.gsub(escaped, "__DOUBLESTAR__", ".*")

-- Special handling for **/name patterns - they should match directories
if string.match(rawPattern, "%*%*/[^/]+$") then
-- **/logs should match files within logs directories
escaped = escaped .. "/"
end

-- Handle trailing slash - directory patterns should match everything within
if string.match(escaped, "/$") then
-- Remove trailing slash and match anything that starts with this path
escaped = string.gsub(escaped, "/$", "/")
-- Don't anchor with $ - allow matching subdirectories
elseif not string.match(escaped, "^/") then
-- Anchor non-directory patterns to match exactly
escaped = escaped .. "$"
end

return escaped
end

-- matches file path substrings
local function isMatch(filePath, pathPattern)
if pathPattern == nil or pathPattern == "" then return false end
if isComment(pathPattern) then return false end

return string.match(filePath, buildEscapedPattern(pathPattern)) ~= nil
local pattern = buildEscapedPattern(pathPattern)
return string.match(filePath, pattern) ~= nil
end

-- Detects `*` pattern
-- Detects `*` pattern (global match - only exact "*")
local function isGlobalMatch(pathPattern)
if pathPattern == nil or pathPattern == "" then return false end
if isComment(pathPattern) then return false end

return string.match(pathPattern, "*") ~= nil
return pathPattern == "*"
end

local function collectCodeowners(group)
Expand Down Expand Up @@ -74,10 +103,10 @@ CO.matchFilesToCodeowner = function(filePaths)
local pathPattern = split[1]

for _, filePath in ipairs(filePaths) do
if isMatch(filePath, pathPattern) then
table.insert(matches, { pathPattern = pathPattern, codeowners = collectCodeowners(split) })
elseif isGlobalMatch(pathPattern) then
if isGlobalMatch(pathPattern) then
globalCodeowners = collectCodeowners(split)
elseif isMatch(filePath, pathPattern) then
table.insert(matches, { pathPattern = pathPattern, codeowners = collectCodeowners(split) })
end
end
end
Expand All @@ -87,8 +116,18 @@ CO.matchFilesToCodeowner = function(filePaths)

sortMatches(matches)

local codeownersList = mapCodeowners(matches)
-- Only use the most specific pattern(s) - those with the longest pathPattern
local maxLength = #matches[1].pathPattern
local mostSpecificMatches = {}
for _, match in ipairs(matches) do
if #match.pathPattern == maxLength then
table.insert(mostSpecificMatches, match)
else
break -- Since sorted by length, we can break early
end
end

local codeownersList = mapCodeowners(mostSpecificMatches)
return codeownersList
end

Expand Down
Loading
Loading