Skip to content

Commit 9c8cdb7

Browse files
author
Fro-Q
committed
feat: headup.nvim version 0.1.0a
1 parent 245fd40 commit 9c8cdb7

File tree

17 files changed

+981
-1081
lines changed

17 files changed

+981
-1081
lines changed

README.md

Lines changed: 100 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -1,235 +1,168 @@
1-
---
2-
last_modified: 2025-11-03 17:39:38
3-
---
4-
51
# headup.nvim
62

7-
![Lua](https://img.shields.io/badge/Made%20with%20Lua-blueviolet.svg?style=for-the-badge&logo=lua)
3+
<p align="center">
4+
5+
*Automatically updates file metadata on save.*
6+
7+
</p>
88

9-
A Neovim plugin that automatically updates file header metadata when files are saved.
9+
## Demo
10+
11+
https://github.com/Fro-Q/headup.nvim/raw/main/assets/headup_demo.mp4
1012

1113
## Features
1214

13-
- **Automatic Updates**: Automatically updates metadata in file headers when saving
14-
- **Respects Manual Edits**: Won't overwrite user manual changes to metadata
15-
- **Configurable**: Support multiple file types with different patterns
15+
- Automatic updates for header metadata on write
16+
- Respects manual edits (won’t overwrite user changes)
17+
- Per-filetype rules with globs, early-stop scanning, and exclusions
18+
19+
## Built-in supported content
1620

17-
## Supported Content
21+
- `current_time`: Current timestamp with customizable format
22+
- `file_size`: Humanized file size (B / KB / MB / GB / TB)
23+
- `line_count`: Number of lines in the buffer
24+
- `file_name`: Base filename
25+
- `file_path`: Path relative to CWD
26+
- `file_path_abs`: Absolute path
1827

19-
- `current_time`: Current timestamp
20-
- `file_size`: File size with automatic unit conversion (B, KB, MB, GB, TB)
21-
- `line_count`: Number of lines in the file
22-
- `file_name`: The base filename of the buffer
23-
- `file_path`: File path relative to current working directory
24-
- `file_path_abs`: Absolute file path
28+
See also: `:help Utils.valid_contents`.
2529

26-
## Installation
30+
## Install
2731

28-
### Using [lazy.nvim](https://github.com/folke/lazy.nvim)
32+
Using lazy.nvim
2933

3034
```lua
3135
{
3236
"Fro-Q/headup.nvim",
3337
config = function()
34-
require("headup").setup({
35-
-- your configuration here
36-
})
38+
require("headup").setup()
3739
end,
3840
}
3941
```
4042

41-
### Using [packer.nvim](https://github.com/wbthomason/packer.nvim)
43+
Using packer.nvim
4244

4345
```lua
4446
use {
4547
"Fro-Q/headup.nvim",
4648
config = function()
47-
require("headup").setup({
48-
-- your configuration here
49-
})
49+
require("headup").setup()
5050
end
5151
}
5252
```
5353

54-
## Configuration
55-
56-
headup.nvim uses a simplified configuration format where you pass global settings and configuration items directly in the setup table, without needing a `configs` wrapper key.
54+
## Quick start
5755

58-
### Basic Structure
56+
Minimal setup that updates something like `-- Last Modified: x` in Lua with current time in the specified format, stopping at the first empty line.
5957

6058
```lua
6159
require("headup").setup({
62-
-- Global settings
6360
enabled = true,
6461
silent = true,
65-
66-
-- Configuration items (array elements)
67-
{
68-
pattern = "*.md", -- file name glob(s) for autocmd
69-
match_pattern = "...", -- Lua pattern to capture value in file content
70-
content = "current_time", -- what to write
71-
-- other options...
72-
},
62+
time_format = "%Y-%m-%d %H:%M:%S",
63+
max_lines = 20,
64+
end_pattern = "^%s*$", -- stop at first empty line
65+
exclude_pattern = "",
7366
{
74-
pattern = {"*.py", "*.pyi"},
75-
match_pattern = "...",
67+
pattern = "*.lua",
68+
match_pattern = "^%s*%-%-%s*[Ll]ast[%s_%-][Mm]odified:%s(.-)%s*$",
7669
content = "current_time",
77-
-- other options...
7870
},
79-
-- Add more configuration items as needed...
8071
})
8172
```
8273

83-
### Default Configuration
74+
## Configuration overview
8475

85-
```lua
86-
require("headup").setup({
87-
enabled = true,
88-
silent = true, -- Set to false to show notifications when updating
76+
- Global options (apply to all items unless overridden):
77+
- `enabled` (boolean, default `true`)
78+
- `silent` (boolean, default `true`)
79+
- `time_format` (string|"inherit")
80+
- `max_lines` (number)
81+
- `end_pattern` (string, Lua pattern; early stop when matched)
82+
- `exclude_pattern` (string|string[]; file globs to skip)
8983

90-
-- Global fallbacks (used when an item doesn't set its own value)
91-
time_format = "inherit",
92-
max_lines = 20,
93-
end_pattern = "^---%s*$", -- Stop scanning at end of YAML front matter
94-
-- exclude_pattern = "*/archive/*", -- optional global exclude
84+
- Per-item options (each element is a rule):
85+
- `pattern` (string|string[]; file globs for autocmd)
86+
- `match_pattern` (string; Lua pattern to capture the value to replace)
87+
- `content` (string; one of supported content types)
88+
- `time_format` (string|"inherit")
89+
- `max_lines` (number)
90+
- `end_pattern` (string)
91+
- `exclude_pattern` (string|string[])
9592

96-
{
97-
pattern = "*.md",
98-
match_pattern = "last_modified:%s*(.-)%s*$",
99-
content = "current_time",
100-
-- This item will use the global fallbacks above
101-
},
102-
})
103-
```
93+
Note: items inherit unset values from global options (fallbacks).
10494

105-
### Configuration Options
95+
## Commands and API
10696

107-
#### Global Options
108-
- `enabled` (boolean): Whether the plugin is enabled globally (default: `true`)
109-
- `silent` (boolean): Whether to suppress notification messages (default: `true`)
110-
- `time_format` (string|"inherit"): Global fallback for time format when `content = "current_time"`
111-
- `max_lines` (number): Global fallback for maximum number of lines to scan from the beginning
112-
- `end_pattern` (string): Global fallback Lua pattern; stop scanning when matched (prevents over-scanning)
113-
- `exclude_pattern` (string|string[]): Global fallback filename glob(s) to exclude from processing
97+
- `:HeadupEnable` / `:HeadupDisable` / `:HeadupToggle`
98+
- `:HeadupUpdate` – force update current buffer (ignores previous cache and buffer state)
99+
- `:HeadupClearCache` – clear internal cache
114100

115-
#### Per-Config Options (as array items)
116-
Each configuration item should be a table with the following fields:
117-
- `pattern` (string|string[]): File name glob(s) for autocmd (e.g., `"*.md"` or `{ "*.md", "*.markdown" }`)
118-
- `match_pattern` (string): Lua pattern to capture the content to update
119-
- `content` (string): What to write (`"current_time"`, `"file_size"`, `"line_count"`, `"file_name"`, `"file_path"`, `"file_path_abs"`)
120-
- `time_format` (string): Time format string for `current_time`, use `"inherit"` to keep original format
121-
- `max_lines` (number): Maximum number of lines to search from the beginning (default: 20)
122-
- `end_pattern` (string, optional): Lua pattern that, when matched, stops scanning further lines (prevents over-scanning)
123-
- `exclude_pattern` (string|string[], optional): File name glob(s) to exclude from processing
101+
Lua API (see :help Headup):
124102

125-
Note: For `time_format`, `max_lines`, `end_pattern`, and `exclude_pattern`, if an item doesn't provide a value, it will fall back to the corresponding global value when set.
103+
- Some commands have equivalent Lua functions:
104+
- `require('headup').enable()` / `disable()` / `toggle()`
105+
- `require('headup').update_current_buffer()`
106+
- `require('headup').clear_cache()`
107+
- You can register custom content generators:
108+
- `require('headup.func').register_generator(name, func)`
126109

127-
### Example Configurations
110+
See more in `:help headup.nvim`.
128111

129-
#### Markdown with YAML Front Matter
112+
## Extend: custom content generators
113+
114+
You can add your own content type by registering a generator. A generator is
115+
`fun(bufnr: integer, ctx?: { time_format?: string, old_content?: string }): string`.
116+
117+
Register:
130118

131119
```lua
132-
{
133-
pattern = "*.md",
134-
match_pattern = "last_modified:%s*(.-)%s*$",
135-
content = "current_time",
136-
time_format = "inherit",
137-
max_lines = 20,
138-
end_pattern = "^---%s*$",
139-
}
120+
local func = require('headup.func')
121+
122+
func.register('my_branch', function(bufnr)
123+
local file = vim.api.nvim_buf_get_name(bufnr)
124+
local dir = file ~= '' and vim.fn.fnamemodify(file, ':h') or vim.fn.getcwd()
125+
local out = vim.fn.systemlist({ 'git', '-C', dir, 'rev-parse', '--abbrev-ref', 'HEAD' })
126+
local branch = (out and out[1]) or 'unknown'
127+
return (branch or ''):gsub('%s+', '')
128+
end)
140129
```
141130

142-
#### Multiple File Types with Different Patterns
131+
Use in config:
143132

144133
```lua
145-
require("headup").setup({
146-
enabled = true,
147-
silent = false, -- Show notifications
148-
149-
-- Markdown files
150-
{
151-
pattern = {"*.md", "*.markdown"},
152-
match_pattern = "last_modified:%s*(.-)%s*$",
153-
content = "current_time",
154-
time_format = "%Y-%m-%d %H:%M:%S",
155-
max_lines = 20,
156-
end_pattern = "^---%s*$",
157-
exclude_pattern = "*/archive/*", -- skip archived notes
158-
},
159-
160-
-- Text files with file size
161-
{
162-
pattern = "*.txt",
163-
match_pattern = "Size:%s*(.-)%s*$",
164-
content = "file_size",
165-
max_lines = 10,
166-
},
167-
168-
-- Any file type with line count
134+
require('headup').setup({
169135
{
170-
pattern = "*",
171-
match_pattern = "Lines:%s*(.-)%s*$",
172-
content = "line_count",
173-
max_lines = 15,
136+
pattern = '*.md',
137+
match_pattern = 'branch:%s*(.-)%s*$',
138+
content = 'my_branch',
174139
},
175140
})
176141
```
177142

178-
## Commands
179-
180-
- `:HeadupEnable` / `:HeadupDisable` / `:HeadupToggle` — control plugin state
181-
- `:HeadupUpdate` — manually update current buffer
182-
- `:HeadupClearCache` — clear internal cache
183-
- `:HeadupShowConfig` — show current effective configuration (formatted table)
184-
185-
The plugin provides the following commands:
186-
187-
- `:HeadupEnable` - Enable the plugin
188-
- `:HeadupDisable` - Disable the plugin
189-
- `:HeadupToggle` - Toggle plugin on/off
190-
- `:HeadupUpdate` - Manually update metadata in current buffer
191-
- `:HeadupClearCache` - Clear internal cache
192-
193-
## How It Works
194-
195-
1. **File Loading**: When a file is opened, the plugin scans for patterns and caches the matched content
196-
2. **Content Detection**: On save, it checks if the file content has actually changed
197-
3. **Manual Edit Respect**: If you manually edited the metadata, the plugin won't overwrite it
198-
4. **Smart Update**: Only updates metadata when file content changed but metadata wasn't manually modified
199-
5. **Cache Update**: Updates the internal cache after successful updates
143+
More templates:
200144

201-
## Example Usage
202-
203-
For a Markdown file with YAML front matter:
204-
205-
```markdown
206-
---
207-
title: My Document
208-
last_modified: 2024-01-01 12:00:00
209-
---
210-
211-
# My Document
212-
213-
Content here...
145+
```lua
146+
-- Uppercase previous value
147+
func.register('uppercase', function(_, ctx)
148+
return ((ctx and ctx.old_content) or ''):upper()
149+
end)
150+
151+
-- SHA256 of current buffer
152+
func.register('file_sha256', function(bufnr)
153+
local text = table.concat(vim.api.nvim_buf_get_lines(bufnr, 0, -1, false), '\n')
154+
return vim.fn.sha256(text)
155+
end)
214156
```
215157

216-
When you edit and save the file, `last_modified` will automatically update to the current timestamp while preserving the original time format.
217-
218-
## Silent Mode
158+
And share your useful generators in [Discussions](https://github.com/Fro-Q/headup.nvim/discussions)!
219159

220-
By default, the plugin operates silently. To see notifications when metadata is updated, set `silent = false` in your configuration:
221-
222-
```lua
223-
require("headup").setup({
224-
silent = false, -- Show notifications
225-
-- other config...
226-
})
227-
```
160+
## Help
228161

229-
When `silent = false`, you'll see notifications like:
230-
- "headup.nvim: Auto-updated timestamp to: 2024-11-02 15:30:45"
231-
- "headup.nvim: Updated file size to: 2.1 KB"
162+
There is a most detailed help file included with the plugin. See in `:help headup.nvim`.
232163

233164
## License
234165

235-
MIT License - see [LICENSE](LICENSE) file for details.
166+
<p align="center">
167+
MIT - Copyright (c) 2025 Fro-Q
168+
</p>

0 commit comments

Comments
 (0)