Skip to content

Commit

Permalink
feat(config)!: new opts, validation, modify setup()
Browse files Browse the repository at this point in the history
- New options, including `hooks` and filters for acwrite buffers! See
  `README.md` for all options.

- `create_parent_dirs` may be set to `false` to disable the automatic
  creation of missing parent directories (such buffers/files will not be
  written then).

- Fully customizable, on-the-fly buffer filtering/exclusion by returning
  `false` in `buf_autosave_pre()` hook.

- Implement type/option module at `sos.type` which provides validation,
  defaults generation, and LSP type generation.

- Strict runtime config checking/validation in `setup()`.

- LSP types (enabling type-checking and autocompletion for
  `setup({...})`) for all options.

`setup()` behavior has changed:

- All-or-nothing: no longer incrementally applies config; each time
  `setup()` is called, what you pass is what you get.

- Now behaves similar to how the `setup()` function of most other neovim
  plugins behave.

BREAKING-CHANGE: `should_observe_buf` is now deprecated, `on_timer` is
no longer public
  • Loading branch information
tmillr committed Oct 15, 2024
1 parent e1e26b7 commit 88c1010
Show file tree
Hide file tree
Showing 15 changed files with 1,751 additions and 241 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ jobs:
${{ env.HOME }}/plenary
key: ${{ steps.restore-cache.outputs.cache-primary-key }}

- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Add nvim to PATH
run: echo ~/nvim/usr/bin >> "${GITHUB_PATH:?}"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
check-formatting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: JohnnyMorganz/stylua-action@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
10 changes: 9 additions & 1 deletion .luarc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@
"workspace": {
"checkThirdParty": false,
"library": ["$VIMRUNTIME", "$PLENARY"],
"ignoreDir": ["/.meta", "/.log", ".vscode"]
"ignoreDir": ["/.meta", "/.log", ".vscode"],
"runtime.special": {
"assertf": "assert"
}
},

"diagnostics": {
"unusedLocalExclude": ["_*", "api"]
},

"type": {
"weakNilCheck": false,
"weakUnionCheck": false
}
}
13 changes: 10 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Variables:
# DIR: path to dir or file to test (default: all of target's files/tests)
# SEQ/SYNC: if set, run tests sequentially (default: true for benchmarks, otherwise false)
# DIR: path to dir or file to test (default: all of target's files/tests)
# SEQ/SYNC: if set, run tests sequentially (default: true for benchmarks, otherwise false)

.PHONY: test t fmt format perf bench checkfmt
.PHONY: test t fmt format perf bench checkfmt types defaults

# because ifdef considers empty vars unset...
ifneq "$(origin SEQ)" "undefined"
Expand Down Expand Up @@ -39,3 +39,10 @@ perf bench: DIR ::= $(or $(DIR),perf)
perf bench: override SEQ ::= true
perf bench:
@$(run-test)

types:
@nvim -l scripts/types.lua

defaults:
@nvim -l scripts/defaults.lua
# @stylua - <<< 'require("sos").setup '"$(nvim -c 'set rtp^=.' -l - <<< 'print(require"sos.config".def:print_default())' 2>&1)"
202 changes: 109 additions & 93 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# 🆘 sos.nvim 🆘 <a href="https://github.com/tmillr/sos.nvim/releases/latest"><img style="padding-left: 4px; padding-top: 20px;" align="right" alt="GitHub release (latest SemVer)" src="https://img.shields.io/github/v/release/tmillr/sos.nvim?label=&logo=semver&sort=semver&color=%2328A745&labelColor=%23384047&logoColor=%23959DA5"/></a><a href="https://github.com/tmillr/sos.nvim/actions/workflows/format.yml"><img style="padding-left: 4px; padding-top: 20px;" align="right" alt="Format" src="https://github.com/tmillr/sos.nvim/actions/workflows/format.yml/badge.svg"/></a><a href="https://github.com/tmillr/sos.nvim/actions/workflows/ci.yml"><img style="padding-left: 0px; padding-top: 20px;" align="right" alt="CI" src="https://github.com/tmillr/sos.nvim/actions/workflows/ci.yml/badge.svg"/></a>
# 🆘 sos.nvim 🆘

<a href="https://github.com/tmillr/sos.nvim/releases/latest"><img alt="GitHub release (latest SemVer)" src="https://img.shields.io/github/v/release/tmillr/sos.nvim?label=&logo=semver&sort=semver&color=%2328A745&labelColor=%23384047&logoColor=%23959DA5"/></a>
<a href="https://github.com/tmillr/sos.nvim/actions/workflows/format.yml"><img alt="Format" src="https://github.com/tmillr/sos.nvim/actions/workflows/format.yml/badge.svg"/></a>
<a href="https://github.com/tmillr/sos.nvim/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/tmillr/sos.nvim/actions/workflows/ci.yml/badge.svg"/></a>

Never manually save/write a buffer again!

Expand All @@ -12,9 +16,10 @@ This plugin is an autosaver for Neovim that automatically saves all of your chan
### Additional Features

- Has its own independent timer, distinct from `'updatetime'`, which may be set to any value in ms
- Timer is only started/reset on buffer changes, not cursor movements or other irrelevant events
- Timer is only started/reset on savable buffer changes, not cursor movements or other irrelevant events
- Keeps buffers in sync with the filesystem by frequently running `:checktime` in the background for you (e.g. on `CTRL-Z` or suspend, resume, command, etc.)
- Intelligently ignores `'readonly'` and other such unwritable buffers/files (i.e. the writing of files with insufficient permissions must be attempted manually with `:w`)
- [Tested](https://github.com/tmillr/sos.nvim/tree/master/tests)

For any questions, help with setup, or general help, you can try [discussions][q&a]. For issues, bugs, apparent bugs, or feature requests, feel free to [open an issue][issues] or [create a pull request][prs].

Expand All @@ -34,109 +39,120 @@ After doing this, you will need to then either restart Neovim or execute `:Plug

## Setup/Options

Listed below are all of the possible options that can be configured along with their default values. Missing options will retain their current value (which will be their default value if never previously set, or if this is the first time calling `setup()` in this Neovim session). This means that `setup()` can be used later on to change just a single option while not touching/changing/resetting to default any of the other options. You can also pass `true` as a 2nd argument to `setup()` (i.e. `setup(opts, true)`) to reset all options to their default values before applying `opts`. If the plugin is started during Neovim's startup/init phase, the plugin will wait until Neovim has finished initializing before setting up its buffer and option observers (autocmds, buffer callbacks, etc.).
Listed below are all of the possible options that can be configured along with their default values. Missing options will use their default value. If the plugin is started during Neovim's startup/init phase, the plugin will wait until Neovim has finished initializing before setting up its buffer and option observers (autocmds, buffer callbacks, etc.).

<!-- BEGIN GENERATED DEFAULTS -->

```lua
require("sos").setup {
-- Whether to enable the plugin
enabled = true,

-- Time in ms after which `on_timer()` will be called. By default, `on_timer()`
-- is called 10 seconds after the last buffer change. Whenever an observed
-- buffer changes, the global timer is started (or reset, if it was already
-- started), and a countdown of `timeout` milliseconds begins. Further buffer
-- changes will then debounce the timer. After firing, the timer is not
-- started again until the next buffer change.
timeout = 10000,

-- Set, and manage, Vim's 'autowrite' option (see :h 'autowrite'). Allowing
-- sos to "manage" the option makes it so that all autosaving functionality
-- can be enabled or disabled altogether in a synchronized fashion as
-- otherwise it is possible for autosaving to still occur even after sos has
-- been explicitly disabled (via :SosDisable for example). There are 3
-- possible values:
--
-- "all": set and manage 'autowriteall'
--
-- true: set and manage 'autowrite'
--
-- false: don't set, touch, or manage any of Vim's 'autowwrite' options
autowrite = true,

-- Automatically write all modified buffers before executing a command on
-- the cmdline. Aborting the cmdline (e.g. via `<Esc>`) also aborts the
-- write. The point of this is so that you don't have to manually write a
-- buffer before running commands such as `:luafile`, `:soruce`, or a `:!`
-- shell command which reads files (such as git or a code formatter).
-- Autocmds will be executed as a result of the writing (i.e. `nested = true`).
--
-- false: don't write changed buffers prior to executing a command
--
-- "all": write on any `:` command that gets executed (but not `<Cmd>`
-- mappings)
--
-- "some": write only if certain commands (source/luafile etc.) appear
-- in the cmdline (not perfect, but may lead to fewer unneeded
-- file writes; implementation still needs some work, see
-- lua/sos/impl.lua)
--
-- table<string, true>: table that specifies which commands should trigger
-- a write
-- keys: the full/long names of commands that should
-- trigger write
-- values: true
save_on_cmd = "some",

-- Save/write a changed buffer before leaving it (i.e. on the `BufLeave`
-- autocmd event). This will lead to fewer buffers having to be written
-- at once when the global/shared timer fires. Another reason for this is
-- the fact that neither `'autowrite'` nor `'autowriteall'` cover this case,
-- so it combines well with those options too.
save_on_bufleave = true,

-- Save all buffers when Neovim loses focus. This is provided because
-- 'autowriteall' does not cover this case. It is particularly useful when
-- swapfiles have been disabled and you (knowingly or unknowingly) start
-- editing the same file in another Neovim instance while having unsaved
-- changes. It helps keep the file/version on the filesystem synchronized
-- with your latest changes when switching applications so that another
-- application won't accidentally open old versions of files that you are
-- still currently editing. Con: it could be that you actually intended to
-- open an older version of a file in another application/Neovim instance,
-- although in that case you're probably better off disabling autosaving
-- altogether (or keep it enabled but utilize a VCS to get the version you
-- need - that is, if you commit frequently enough). This option also enables
-- saving on suspend.
save_on_focuslost = true,

-- Predicate fn which receives a buf number and should return true if it
-- should be observed for changes (i.e. whether the buffer should debounce
-- the shared/global timer). You probably don't want to change this unless
-- you absolutely need to and know what you're doing. Setting this option
-- will replace the default fn/behavior which is to observe buffers which
-- have: a normal 'buftype', 'ma', 'noro'. See lua/sos/impl.lua for the
-- default behavior/fn.
---@type fun(bufnr: integer): boolean
-- should_observe_buf = require("sos.impl").should_observe_buf,

-- The function that is called when the shared/global timer fires. You
-- probably don't want to change this unless you absolutely need to and know
-- what you're doing. Setting this option will replace the default
-- fn/behavior, which is simply to write all modified (i.e. 'mod' option is
-- set) buffers. See lua/sos/impl.lua for the default behavior/fn. Any value
-- returned by this function is ignored. `vim.api.*` can be used inside this
-- fn (this fn will be called with `vim.schedule()`).
-- on_timer = require("sos.impl").on_timer,
require('sos').setup {
---Whether to enable the plugin.
enabled = true,

---Timeout in milliseconds for the global timer. Buffer changes debounce the
---timer.
timeout = 10000,

---Automatically create missing parent directories when writing/autosaving a
---buffer.
create_parent_dirs = true,

---Whether to set and manage Vim's 'autowrite' option.
---
---### Choices:
---
--- - "all": set and manage 'autowriteall'
--- - true : set and manage 'autowrite'
--- - false: don't set or manage any of Vim's 'autowwrite' options
autowrite = true,

---Save all buffers before executing a `:` command on the cmdline (does not
---include `<Cmd>` mappings).
---
---### Choices:
---
--- - "all" : save on any cmd that gets executed
--- - "some" : only for some commands (source, luafile, etc.).
--- not perfect, but may lead to fewer unnecessary
--- file writes compared to `"all"`.
--- - table<string, boolean>: map specifying which commands trigger a save
--- where keys are the full command names
--- - false : never/disable
save_on_cmd = 'some',

---Save current buffer on `BufLeave`. See `:help BufLeave`.
save_on_bufleave = true,

---Save all buffers when Neovim loses focus or is suspended.
save_on_focuslost = true,

should_save = {
---Whether to autosave buffers which aren't modifiable.
---See `:help 'modifiable'`.
unmodifiable = true,

---How to handle `acwrite` type buffers (i.e. where `vim.bo.buftype ==
---"acwrite"` or the buffer's name is a URI). These buffers use an autocmd to
---perform special actions and side-effects when saved/written.
acwrite = {
---Whether to autosave buffers which perform network actions (such as sending a
---request) on save/write. E.g. `scp`, `http`
net = true,

---Whether to autosave buffers which perform git actions (such as staging
---buffer content) on save/write. E.g. `fugitive`, `diffview`, `gitsigns`
git = true,

---Whether to autosave buffers which process the file on save/write.
---E.g. `tar`, `zip`, `gzip`
compress = true,

---Whether to autosave `acwrite` buffers which don't match any of the other
---acwrite criteria/filters.
other = true,

---URI schemes to allow/disallow autosaving for. If a scheme is set to `false`,
---any buffer whose name begins with that scheme will not be autosaved.
---Provided schemes should be lowercase and will be matched case-insensitively.
---Schemes take precedence over other `acwrite` filters.
---
---Example:
---
---```lua
---schemes = { http = false, octo = false, file = true }
---```
schemes = {
---Octo buffers are disabled by default as they can create new
---issues, PR's, and comments on write/save.
octo = false,
term = false,
file = true,
},
},
},

hooks = {
---A function – or any other callable value – which is called just before
---writing/autosaving a buffer. If `false` is returned, the buffer will not be
---written.
buf_autosave_pre = function(bufnr, bufname) end,

---A function – or any other callable value – which is called just after
---writing/autosaving a buffer (even if the write failed).
buf_autosave_post = function(bufnr, bufname, errmsg) end,
},
}
```

<!-- END GENERATED DEFAULTS -->

## Commands

All of the available commands are defined [here](/lua/sos/commands.lua).

## Tips

- Decrease the `timeout` value if you'd like more frequent/responsive autosaving behavior (e.g. `10000` for 10 seconds, or `5000` for 5 seconds). It's probably best not to go below 5 seconds however.
- Disable swapfiles.

## FAQ

Expand Down
2 changes: 1 addition & 1 deletion lua/sos/autocmds.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ local augroup = 'sos-autosaver'
function M.clear() api.nvim_create_augroup(augroup, { clear = true }) end

---Update defined autocmds according to `cfg`
---@param cfg sos.Config
---@param cfg sos.config.opts
---@return nil
function M.refresh(cfg)
api.nvim_create_augroup(augroup, { clear = true })
Expand Down
26 changes: 17 additions & 9 deletions lua/sos/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -80,31 +80,39 @@ function M.resolve_bufspec(info)
return buf
end

local function verbose(opts)
if opts.smods.emsg_silent or opts.smods.silent then
return opts.smods.unsilent
end

return true
end

return setmetatable(
Commands {
SosEnable = {
desc = 'Enable sos autosaver',
nargs = 0,
force = true,
function() require('sos').enable(true) end,
function(opts) require('sos').enable(verbose(opts)) end,
},

SosDisable = {
desc = 'Disable sos autosaver',
nargs = 0,
force = true,
function() require('sos').disable(true) end,
function(opts) require('sos').disable(verbose(opts)) end,
},

SosToggle = {
desc = 'Toggle sos autosaver',
nargs = 0,
force = true,
function()
if require('sos.config').enabled then
require('sos').disable(true)
function(opts)
if require('sos.config').opts.enabled then
require('sos').disable(verbose(opts))
else
require('sos').enable(true)
require('sos').enable(verbose(opts))
end
end,
},
Expand All @@ -116,9 +124,9 @@ return setmetatable(
addr = 'buffers',
complete = 'buffer',
force = true,
function(info)
local buf = M.resolve_bufspec(info)
if buf then require('sos').toggle_buf(buf, true) end
function(opts)
local buf = M.resolve_bufspec(opts)
if buf then require('sos').toggle_buf(buf, verbose(opts)) end
end,
},
},
Expand Down
Loading

0 comments on commit 88c1010

Please sign in to comment.