Skip to content

Commit

Permalink
feat: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sQVe committed Apr 13, 2023
0 parents commit f90a249
Show file tree
Hide file tree
Showing 14 changed files with 341 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 Oskar Grunning

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# bufignore
24 changes: 24 additions & 0 deletions lua/bufignore/config.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
local defaults = require('bufignore.defaults')

--- @class Config
--- @field private _user_config UserConfig
local M = {}

M._user_config = nil

--- Gets the user's configuration.
--- @return UserConfig config The current configuration.
M.get_user_config = function()
return M._user_config
end

--- Sets up the configuration.
--- @param overrides? UserConfig Configration which will override the defaults.
--- @return UserConfig config The configuration.
M.setup = function(overrides)
M._user_config = vim.tbl_deep_extend('force', defaults, overrides or {})

return M._user_config
end

return M
11 changes: 11 additions & 0 deletions lua/bufignore/defaults.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--- @alias ConfigCallback fun(event: { bufnr: number, file_path: string }): boolean

--- @class UserConfig
--- @field auto_start boolean
--- @field callback ConfigCallback | false | nil TODO
local defaults = {
auto_start = true,
callback = nil,
}

return defaults
51 changes: 51 additions & 0 deletions lua/bufignore/dispatcher.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
local queue = require('bufignore.queue')

local augroup =
vim.api.nvim_create_augroup('bufignore-augroup', { clear = true })

--- @class Dispatcher
--- @field private _is_bound boolean
local M = {}

M._is_bound = false

--- Binds event listeners for the plugin.
M.bind_events = function()
if not M._is_bound then
-- Listen for `BufHidden` events and enqueue the file for
-- processing.
vim.api.nvim_create_autocmd('BufHidden', {
group = augroup,
callback = function(event)
queue.enqueue_file(vim.fn.fnamemodify(event.file, ':p'))
end,
})

-- Listen for `DirChanged` events and enqueue all buffers for
-- processing.
vim.api.nvim_create_autocmd('DirChanged', {
group = augroup,
callback = function()
local bufnrs = vim.api.nvim_list_bufs()

queue.clear_queue()
for _, bufnr in ipairs(bufnrs) do
queue.enqueue_file(vim.api.nvim_buf_get_name(bufnr))
end
end,
})

M._is_bound = true
end
end

--- Unbinds event listeners for the plugin.
M.unbind_events = function()
if M._is_bound then
vim.api.nvim_clear_autocmds({ group = augroup })

M._is_bound = false
end
end

return M
52 changes: 52 additions & 0 deletions lua/bufignore/file-processor.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
local config = require('bufignore.config')

--- @class FileProcessor
local M = {}

--- Checks if a buffer meets the requirements for unlisting.
--- @param bufnr number The buffer number.
--- @param file_path string The path of the file associated with the buffer.
--- @return boolean `true` if the buffer meets the unlisting requirements, otherwise `false`.
M._is_valid_for_unlisting = function(bufnr, file_path)
local user_config = config.get_user_config()

-- Buffer is not visible
return vim.fn.bufwinid(bufnr) == -1
-- Buffer is valid
and vim.api.nvim_buf_is_valid(bufnr)
-- Buffer is loaded
and vim.api.nvim_buf_is_loaded(bufnr)
-- Buffer is listed
and vim.api.nvim_buf_get_option(bufnr, 'buflisted')
-- User-defined callback is either falsy or returns true.
and (
not user_config.callback
or user_config.callback({ bufnr = bufnr, file_path = file_path })
)
end

--- Unlists a buffer for an ignored file.
--- @param file_path string The path of the ignored file.
--- @private
M._unlist_ignored_file = function(file_path)
-- Schedules main event-loop invocation to preventing textlock issues.
vim.schedule(function()
---@diagnostic disable-next-line: param-type-mismatch
local bufnr = vim.fn.bufnr(file_path)

if M._is_valid_for_unlisting(bufnr, file_path) then
-- Unlist git ignored file buffer.
vim.api.nvim_buf_set_option(bufnr, 'buflisted', false)
end
end)
end

--- Unlists buffers for ignored files.
---@param file_paths string[] The paths of the ignored files.
M.unlist_ignored_files = function(file_paths)
for _, file_path in ipairs(file_paths) do
M._unlist_ignored_file(file_path)
end
end

return M
36 changes: 36 additions & 0 deletions lua/bufignore/git.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
local Job = require('plenary.job')

--- @class Git
local M = {}

--- Checks if files are ignored by Git.
--- @param file_paths string[] The list of file paths to check for ignore status.
--- @param callback function The callback function that receives the ignored file paths.
M.check_ignore = function(file_paths, callback)
local args = { 'check-ignore' }

for _, file_path in ipairs(file_paths) do
table.insert(args, file_path)
end

-- Execute the Git command.
Job:new({
command = 'git',
args = args,
on_exit = function(_, exit_code)
if exit_code > 0 then
callback({})
end
end,
on_stdout = function(_, data)
local ignored_file_paths = {}
for line in data:gmatch('([^\n]+)') do
table.insert(ignored_file_paths, line)
end

callback(ignored_file_paths)
end,
}):start()
end

return M
27 changes: 27 additions & 0 deletions lua/bufignore/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
local config = require('bufignore.config')
local dispatcher = require('bufignore.dispatcher')

--- @class Init
local M = {}

--- Sets up the plugin.
--- @param overrides? UserConfig Configration which will override the defaults.
M.setup = function(overrides)
local user_config = config.setup(overrides)

if user_config.auto_start then
M.start()
end
end

--- Starts the plugin.
M.start = function()
dispatcher.bind_events()
end

--- Stops the plugin.
M.stop = function()
dispatcher.unbind_events()
end

return M
61 changes: 61 additions & 0 deletions lua/bufignore/queue.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
local git = require('bufignore.git')
local file_processor = require('bufignore.file-processor')

--- @class Queue
--- @field private _queue string[]
--- @field private _is_running boolean
--- @field private _throttle_ms number
local M = {}

M._is_running = false
M._queue = {}
M._throttle_ms = 400

--- Validates a file.
--- @param file_path string The file path to validate.
--- @return boolean valid_file_path `true` if the file path is valid, otherwise `false`.
--- @private
M._is_valid_file = function(file_path)
local is_non_empty_file_path = file_path ~= nil and file_path:len() > 0
local is_file_path_in_cwd = vim.startswith(file_path, vim.fn.getcwd())

return is_non_empty_file_path and is_file_path_in_cwd
end

--- Clears the event queue.
M.clear_queue = function()
M._queue = {}
M._is_running = false
end

--- Adds an event to the queue, and starts processing if not already running.
--- @param file_path string
M.enqueue_file = function(file_path)
if M._is_valid_file(file_path) then
local absolute_file_path = vim.fn.fnamemodify(file_path, ':p')

table.insert(M._queue, absolute_file_path)

M._process_queue()
end
end

--- Processes pending file paths in the queue after the throttle time, if not already running.
--- @private
M._process_queue = function()
if M._is_running then
return
end

M._is_running = true

vim.defer_fn(function()
local pending_files = vim.deepcopy(M._queue)

git.check_ignore(pending_files, file_processor.unlist_ignored_files)

M.clear_queue()
end, M._throttle_ms)
end

return M
31 changes: 31 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "bufignore.nvim",
"version": "1.0.0",
"author": "Oskar Grunning",
"description": "Unlist hidden buffers that are git ignored.",
"homepage": "https://github.com/sQVe/bufignore.nvim",
"repository": "github:sQVe/bufignore.nvim#readme",
"license": "MIT",
"devDependencies": {
"prettier": "latest"
}
}
4 changes: 4 additions & 0 deletions stylua.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
column_width = 80
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferSingle"

0 comments on commit f90a249

Please sign in to comment.