Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/fabridamicelli/cronex.nvim
Human-readable cron expressions in Neovim
https://github.com/fabridamicelli/cronex.nvim
cron cron-described cron-explained cron-expression human-readable human-readable-cron-expression neovim
Last synced: about 11 hours ago
JSON representation
Human-readable cron expressions in Neovim
- Host: GitHub
- URL: https://github.com/fabridamicelli/cronex.nvim
- Owner: fabridamicelli
- License: mit
- Created: 2024-04-13T12:55:08.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2024-04-13T16:19:30.000Z (7 months ago)
- Last Synced: 2024-04-14T08:01:55.456Z (7 months ago)
- Topics: cron, cron-described, cron-explained, cron-expression, human-readable, human-readable-cron-expression, neovim
- Language: Lua
- Homepage:
- Size: 1.42 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[![CI](https://github.com/fabridamicelli/cronex.nvim/actions/workflows/ci.yml/badge.svg)](https://github.com/fabridamicelli/cronex.nvim/actions/workflows/ci.yml)
If you find this work useful, don't forget to give it a GitHub ⭐ to help others find and trust it!
# cronex.nvim
Human-readable cron expressions in Neovim## What is Cronex
Cronex is a Neovim plugin to render in-line, human-readable explanations of [cron expressions](https://en.wikipedia.org/wiki/Cron):[Here's a short introduction video](https://www.youtube.com/live/Se8SKgmCnkc?t=31717s).
This plugin is **not** a cron expression parser/checker by itself.
Cronex is rather the "client" that allows the Neovim user to integrate and customize "servers" (cron expression "explainers") in a flexible fashion.
There are several implementations of those out there (see below).
You can use any of those with Cronex.## Getting Started
### Dependencies
You will need a cron expression explainer installed.
The default is [cronstrue](https://www.npmjs.com/package/cronstrue), which is the one used by the [vscode package `cron-explained`](https://marketplace.visualstudio.com/items?itemName=tumido.cron-explained).[Install the `cronstrue` library](https://www.npmjs.com/package/cronstrue) and make sure that the command `cronstrue` is available in the environment where your buffer is being shown.
That will use the `cronstrue` library under the hood to generate the explanations.
### Installation
Using [lazy.nvim](https://github.com/folke/lazy.nvim)```lua
-- init.lua:
{
'fabridamicelli/cronex.nvim',
opts = {},
}-- Or
-- plugins/cronex.lua:
return {
'fabridamicelli/cronex.nvim',
opts = {},
}
```Using [vim-plug](https://github.com/junegunn/vim-plug)
```viml
call plug#begin()
Plug 'fabridamicelli/cronex.nvim'
call plug#end()lua < Every minute
-- Any command that is available in your command line can be used here.
-- examples:
-- "/path/to/miniconda3/envs/neovim/bin/cronstrue" (point to a conda virtualenv)
-- "python explainer.py" (assuming you have such a python script available)
cmd = "cronstrue",
-- Optional arguments to pass to the command
-- eg: "/path/to/a/go/binary" (assuming you have a go binary)
-- args = { "-print-all" } (assuming the program understands the flag 'print-all')
args = {}
},
-- Configure the post-processing of the explanation string.
-- eg: transform "* * * * *": Every minute --to--> Every minute
-- using require("cronex.format").all_after_colon,
format = function(s)
return s
end
})
```To embed the above configuration code snippet in a `.vim` file
(for example in `init.vim`),
wrap it in `lua << EOF code-snippet EOF`:```lua
lua << EOF
require('cronex').setup{
-- ...
}
EOF
```### Extractor
Logic of the default extractor can be found here in `/cronex/cron_from_line.lua`.
Default extractor searches for at most 1 expression per line of length 7, 6 or 5 (in that order).
But Cronex allows the user to hook in and swap this by any arbitrary logic.The extractor has 2 parts: `cron_from_line` and `extract`, both are functions.
You can swap any or both of the two with custom functions, provided you respect the following interfaces:`cron_from_line`: Function with signature `string -> string|nil`.
Returns the cron expression if found (else `nil`)`extract`: Function with signature `function -> table`
The input `function` processes each buffer line (may be identity, i.e. just return line as is).
Output `table` of pairs (`line_number`, `cron_expression`), empty if no cron expressions found.Here's a toy example on how to customize the functions, that will set "line says --> hello world" on every line of the buffer:
```lua
require("cronex").setup({
extractor = {
cron_from_line = function(_)
-- This commented block is what you would actually do in a real scenario
-- local cron = do_something_to_extract_cron(buffer_line)
-- if cron then
-- return cron
-- end
-- return nil
return "hello world" -- let's hard-code something :)
end,
extract = function(cron_from_line)
local t = {}
for i, line in ipairs(vim.api.nvim_buf_get_lines(0, 0, -1, false)) do
t[i - 1] = string.format("line %s says --> %s ", i - 1, cron_from_line(line))
end
return t
end
},
explainer = {
cmd = "echo" -- just echo the what extract produces
},
})
```
Under the hood, `cron_from_line` will be passed to `extract` like so:
```lua
extract(cron_from_line)
```
This allows you to plug a custom function to extract cron from line and still use the default `extract` functionYou may even just set `cron_from_line` to `nil` and use the `extract` function to send the whole buffer to another program from which you capture the output.
All that matters is that `extract` returns the table with pairs (`line_number`, `cron_expression`).
For example:
```lua
{
require("cronex").setup({
extractor = {
cron_from_line = nil,
extract = function(_)
local t = {}
local out = send_buffer_to_external_program_and_collect_crons()
for lnum, cron in out do
t[lnum] = cron
end
return t
end
}})
}
```### Explainer
As already mentioned above, Cronex is the integrates the functionality of external cron expression explainers into Neovim.
There are several implementations of those [out there]().More generally, it's up to the user which explainer program to use in the background.
Cronex will call such program via the command (`cmd`), collect the output and pass it along to Neovim.
This is the default:
```lua
require("cronex").setup({
explainer = {
cmd = "cronstrue",
args = {}
}
})
```
For example, you can have `cronstrue` installed in a conda virtualenv.
If the virtualenv is active, everything should work out of the box.
But you may not want to install `cronstrue` in every virtualenv, so you can have only one central environment with `cronstrue` installed and point to that binary explicitly in the config:
```lua
{
require("cronex").setup({
explainer = {
cmd = "/home/username/miniconda3/envs/neovim/bin/cronstrue"
})
}
```
In fact `cmd` can call anything that knows how to deal with the cron expression.
For example, calling a go program:
```lua
{
require("cronex").setup({
explainer = {
cmd = { "go", "run", "/path/to/go/app/cmd/module/main.go" },
-- or if you pre-compiled it (recommended):
-- cmd = { "/path/to/go/app/cmd/module/binary" },
args = { "-arg1", "-arg2" }
}
})
}
```Here are a few of those third-party libraries as well as the Cronex command to use them:
***`cronstrue:`***
This is the default explainer by Cronex and the very same library used by the [vscode package `cron-explained`](https://marketplace.visualstudio.com/items?itemName=tumido.cron-explained).
You need first install the [`cronstrue` library](https://www.npmjs.com/package/cronstrue) and make sure that the command `cronstrue` is available in the environment where your buffer is being shown.
For example, you can use it inside of a Python virtual environment (in this case managed by conda to install `nodejs`):
```bash
conda create -n venv
conda activate venv
conda install nodejs -c conda-forge
npm install cronstrue
```
After that `cronstrue` will only be installed inside `venv` (thus only available there).***`hcron:`***
This explainer is written in Go and much considerably faster than the default.
But it is not as widely used and the project does not seem to be that well maintained.
Recommendation: Compile the binaryHere's a (non-exhaustive) overview cron explainers out there:
| Language | Link |
|------------| --------------------------------------------------------|
| JavaScript | https://github.com/bradymholt/cronstrue |
| Go | https://github.com/lnquy/cron |
| Python | https://github.com/Salamek/cron-descriptor |
| .NET | https://github.com/bradymholt/cron-expression-descriptor|
| Java | https://github.com/grahamar/cron-parser |
| Rust | https://github.com/zslayton/cron |### format
We might want to modify the output from the third-party explainer libraries.
For example, some explainers show the input as well in the output like so:
```
"* * * * *": Every minute
```
In that case, you could use the function `require("cronex.format").all_after_colon`) to transform the output to just show "Every minute".
But the user can do any other transformation by defining a lua function, for example:
```lua
{
require("cronex").setup({
format = function(explanation)
local colon = string.find(explanation, ": ")
if colon then
return "Human-readable:" .. string.sub(explanation, colon + 2)
end
return explanation
end
})
}
```
That will transform it like this:```
"* * * * *": Every minute --> Human-readable: Every minute
```## Limitations
The current `extract` logic is a bit rudimentary (partly because regex in lua are a bit trickier than normal (at least for me).
Any improvement along those lines is more than welcome.The call to the explainer is a blocking operation.
While testing I found that to be a problem only if there are unrealistically many cron expressions in the buffer.
Also, the Go implementation of the explainer is so fast that even having hundreds of expressions in a buffer everything runs decently fast.
In short, my guess is that almost no user (if any at all) will notice this.
Having said that, a few potential ideas to improve performance:
- Accelerating extraction, for example, by using `ripgrep` to extract all crons in one shot (instead of iterating over lines)
- Implementing the explainer in pure lua to avoid external calls## Troubleshooting
- Default behaviour considers only 1 cron expression is per line.
So having repeated expressions in one line will result in no explanation at all.
I haven't seen use-cases where it makes sense to have more than one, but I'd be open to consider it if that makes sense.
- If encountering problems with an expression, disable the format in order to see the exact output coming from the explainer