├── LICENSE
├── README.md
├── doc
└── mini-trailspace.txt
└── lua
└── mini
└── trailspace.lua
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Evgeni Chasnovski
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### Work with trailing whitespace
4 |
5 | See more details in [Features](#features) and [Documentation](doc/mini-trailspace.txt).
6 |
7 | ---
8 |
9 | > [!NOTE]
10 | > This was previously hosted at a personal `echasnovski` GitHub account. It was transferred to a dedicated organization to improve long term project stability. See more details [here](https://github.com/nvim-mini/mini.nvim/discussions/1970).
11 |
12 | ⦿ This is a part of [mini.nvim](https://github.com/nvim-mini/mini.nvim) library. Please use [this link](https://github.com/nvim-mini/mini.nvim/blob/main/readmes/mini-trailspace.md) if you want to mention this module.
13 |
14 | ⦿ All contributions (issues, pull requests, discussions, etc.) are done inside of 'mini.nvim'.
15 |
16 | ⦿ See the repository page to learn about common design principles and configuration recipes.
17 |
18 | ---
19 |
20 | If you want to help this project grow but don't know where to start, check out [contributing guides of 'mini.nvim'](https://github.com/nvim-mini/mini.nvim/blob/main/CONTRIBUTING.md) or leave a Github star for 'mini.nvim' project and/or any its standalone Git repositories.
21 |
22 | ## Demo
23 |
24 |
25 | https://user-images.githubusercontent.com/24854248/173045420-7aaf21b6-1d2e-4333-8a23-dea7e49c3a01.mp4
26 |
27 | ## Features
28 |
29 | - Highlighting is done only in modifiable buffer by default, only in Normal mode, and stops in Insert mode and when leaving window.
30 | - Trim all trailing whitespace with `MiniTrailspace.trim()`.
31 | - Trim all trailing empty lines with `MiniTrailspace.trim_last_lines()`.
32 |
33 | ## Installation
34 |
35 | This plugin can be installed as part of 'mini.nvim' library (**recommended**) or as a standalone Git repository.
36 |
37 | There are two branches to install from:
38 |
39 | - `main` (default, **recommended**) will have latest development version of plugin. All changes since last stable release should be perceived as being in beta testing phase (meaning they already passed alpha-testing and are moderately settled).
40 | - `stable` will be updated only upon releases with code tested during public beta-testing phase in `main` branch.
41 |
42 | Here are code snippets for some common installation methods (use only one):
43 |
44 |
45 | With mini.deps
46 |
47 | - 'mini.nvim' library:
48 |
49 | | Branch | Code snippet |
50 | |--------|-----------------------------------------------|
51 | | Main | *Follow recommended ‘mini.deps’ installation* |
52 | | Stable | *Follow recommended ‘mini.deps’ installation* |
53 |
54 | - Standalone plugin:
55 |
56 | | Branch | Code snippet |
57 | |--------|----------------------------------------------------------------------|
58 | | Main | `add(‘nvim-mini/mini.trailspace’)` |
59 | | Stable | `add({ source = ‘nvim-mini/mini.trailspace’, checkout = ‘stable’ })` |
60 |
61 |
62 |
63 |
64 | With folke/lazy.nvim
65 |
66 | - 'mini.nvim' library:
67 |
68 | | Branch | Code snippet |
69 | |--------|-----------------------------------------------|
70 | | Main | `{ 'nvim-mini/mini.nvim', version = false },` |
71 | | Stable | `{ 'nvim-mini/mini.nvim', version = '*' },` |
72 |
73 | - Standalone plugin:
74 |
75 | | Branch | Code snippet |
76 | |--------|-----------------------------------------------------|
77 | | Main | `{ 'nvim-mini/mini.trailspace', version = false },` |
78 | | Stable | `{ 'nvim-mini/mini.trailspace', version = '*' },` |
79 |
80 |
81 |
82 |
83 | With junegunn/vim-plug
84 |
85 | - 'mini.nvim' library:
86 |
87 | | Branch | Code snippet |
88 | |--------|------------------------------------------------------|
89 | | Main | `Plug 'nvim-mini/mini.nvim'` |
90 | | Stable | `Plug 'nvim-mini/mini.nvim', { 'branch': 'stable' }` |
91 |
92 | - Standalone plugin:
93 |
94 | | Branch | Code snippet |
95 | |--------|------------------------------------------------------------|
96 | | Main | `Plug 'nvim-mini/mini.trailspace'` |
97 | | Stable | `Plug 'nvim-mini/mini.trailspace', { 'branch': 'stable' }` |
98 |
99 |
100 |
101 | **Important**: don't forget to call `require('mini.trailspace').setup()` to enable its functionality.
102 |
103 | **Note**: if you are on Windows, there might be problems with too long file paths (like `error: unable to create file : Filename too long`). Try doing one of the following:
104 |
105 | - Enable corresponding git global config value: `git config --system core.longpaths true`. Then try to reinstall.
106 | - Install plugin in other place with shorter path.
107 |
108 | ## Default config
109 |
110 | ```lua
111 | -- No need to copy this inside `setup()`. Will be used automatically.
112 | {
113 | -- Highlight only in normal buffers (ones with empty 'buftype'). This is
114 | -- useful to not show trailing whitespace where it usually doesn't matter.
115 | only_in_normal_buffers = true,
116 | }
117 | ```
118 |
119 | ## Similar plugins
120 |
121 | - [ntpeters/vim-better-whitespace](https://github.com/ntpeters/vim-better-whitespace)
122 |
--------------------------------------------------------------------------------
/doc/mini-trailspace.txt:
--------------------------------------------------------------------------------
1 | *mini.trailspace* Trailspace (highlight and remove)
2 |
3 | MIT License Copyright (c) 2021 Evgeni Chasnovski
4 |
5 | ------------------------------------------------------------------------------
6 | *MiniTrailspace*
7 | Features:
8 | - Highlighting is done only in modifiable buffer by default, only in Normal
9 | mode, and stops in Insert mode and when leaving window.
10 |
11 | - Trim all trailing whitespace with |MiniTrailspace.trim()|.
12 |
13 | - Trim all trailing empty lines with |MiniTrailspace.trim_last_lines()|.
14 |
15 | # Setup ~
16 |
17 | This module needs a setup with `require('mini.trailspace').setup({})`
18 | (replace `{}` with your `config` table). It will create global Lua table
19 | `MiniTrailspace` which you can use for scripting or manually (with
20 | `:lua MiniTrailspace.*`).
21 |
22 | See |MiniTrailspace.config| for `config` structure and default values.
23 |
24 | You can override runtime config settings locally to buffer inside
25 | `vim.b.minitrailspace_config` which should have same structure as
26 | `MiniTrailspace.config`. See |mini.nvim-buffer-local-config| for more details.
27 |
28 | # Highlight groups ~
29 |
30 | - `MiniTrailspace` - highlight group for trailing space.
31 |
32 | To change any highlight group, set it directly with |nvim_set_hl()|.
33 |
34 | # Disabling ~
35 |
36 | To disable, set `vim.g.minitrailspace_disable` (globally) or
37 | `vim.b.minitrailspace_disable` (for a buffer) to `true`. Considering high
38 | number of different scenarios and customization intentions, writing exact
39 | rules for disabling module's functionality is left to user. See
40 | |mini.nvim-disabling-recipes| for common recipes. Note: after disabling
41 | there might be highlighting left; it will be removed after next
42 | highlighting update (see |events| and `MiniTrailspace` |:augroup|).
43 |
44 | ------------------------------------------------------------------------------
45 | *MiniTrailspace.setup()*
46 | `MiniTrailspace.setup`({config})
47 | Module setup
48 |
49 | Parameters ~
50 | {config} `(table|nil)` Module config table. See |MiniTrailspace.config|.
51 |
52 | Usage ~
53 | >lua
54 | require('mini.trailspace').setup() -- use default config
55 | -- OR
56 | require('mini.trailspace').setup({}) -- replace {} with your config table
57 | <
58 | ------------------------------------------------------------------------------
59 | *MiniTrailspace.config*
60 | `MiniTrailspace.config`
61 | Defaults ~
62 | >lua
63 | MiniTrailspace.config = {
64 | -- Highlight only in normal buffers (ones with empty 'buftype'). This is
65 | -- useful to not show trailing whitespace where it usually doesn't matter.
66 | only_in_normal_buffers = true,
67 | }
68 | <
69 | ------------------------------------------------------------------------------
70 | *MiniTrailspace.highlight()*
71 | `MiniTrailspace.highlight`()
72 | Highlight trailing whitespace in current window
73 |
74 | ------------------------------------------------------------------------------
75 | *MiniTrailspace.unhighlight()*
76 | `MiniTrailspace.unhighlight`()
77 | Unhighlight trailing whitespace in current window
78 |
79 | ------------------------------------------------------------------------------
80 | *MiniTrailspace.trim()*
81 | `MiniTrailspace.trim`()
82 | Trim trailing whitespace
83 |
84 | ------------------------------------------------------------------------------
85 | *MiniTrailspace.trim_last_lines()*
86 | `MiniTrailspace.trim_last_lines`()
87 | Trim last blank lines
88 |
89 |
90 | vim:tw=78:ts=8:noet:ft=help:norl:
--------------------------------------------------------------------------------
/lua/mini/trailspace.lua:
--------------------------------------------------------------------------------
1 | --- *mini.trailspace* Trailspace (highlight and remove)
2 | ---
3 | --- MIT License Copyright (c) 2021 Evgeni Chasnovski
4 |
5 | --- Features:
6 | --- - Highlighting is done only in modifiable buffer by default, only in Normal
7 | --- mode, and stops in Insert mode and when leaving window.
8 | ---
9 | --- - Trim all trailing whitespace with |MiniTrailspace.trim()|.
10 | ---
11 | --- - Trim all trailing empty lines with |MiniTrailspace.trim_last_lines()|.
12 | ---
13 | --- # Setup ~
14 | ---
15 | --- This module needs a setup with `require('mini.trailspace').setup({})`
16 | --- (replace `{}` with your `config` table). It will create global Lua table
17 | --- `MiniTrailspace` which you can use for scripting or manually (with
18 | --- `:lua MiniTrailspace.*`).
19 | ---
20 | --- See |MiniTrailspace.config| for `config` structure and default values.
21 | ---
22 | --- You can override runtime config settings locally to buffer inside
23 | --- `vim.b.minitrailspace_config` which should have same structure as
24 | --- `MiniTrailspace.config`. See |mini.nvim-buffer-local-config| for more details.
25 | ---
26 | --- # Highlight groups ~
27 | ---
28 | --- - `MiniTrailspace` - highlight group for trailing space.
29 | ---
30 | --- To change any highlight group, set it directly with |nvim_set_hl()|.
31 | ---
32 | --- # Disabling ~
33 | ---
34 | --- To disable, set `vim.g.minitrailspace_disable` (globally) or
35 | --- `vim.b.minitrailspace_disable` (for a buffer) to `true`. Considering high
36 | --- number of different scenarios and customization intentions, writing exact
37 | --- rules for disabling module's functionality is left to user. See
38 | --- |mini.nvim-disabling-recipes| for common recipes. Note: after disabling
39 | --- there might be highlighting left; it will be removed after next
40 | --- highlighting update (see |events| and `MiniTrailspace` |:augroup|).
41 | ---@tag MiniTrailspace
42 |
43 | -- Module definition ==========================================================
44 | local MiniTrailspace = {}
45 | local H = {}
46 |
47 | --- Module setup
48 | ---
49 | ---@param config table|nil Module config table. See |MiniTrailspace.config|.
50 | ---
51 | ---@usage >lua
52 | --- require('mini.trailspace').setup() -- use default config
53 | --- -- OR
54 | --- require('mini.trailspace').setup({}) -- replace {} with your config table
55 | --- <
56 | MiniTrailspace.setup = function(config)
57 | -- Export module
58 | _G.MiniTrailspace = MiniTrailspace
59 |
60 | -- Setup config
61 | config = H.setup_config(config)
62 |
63 | -- Apply config
64 | H.apply_config(config)
65 |
66 | -- Define behavior
67 | H.create_autocommands(config)
68 |
69 | -- Create default highlighting
70 | H.create_default_hl()
71 |
72 | -- Initialize highlight (usually takes effect during startup)
73 | vim.defer_fn(MiniTrailspace.highlight, 0)
74 | end
75 |
76 | --- Defaults ~
77 | ---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
78 | MiniTrailspace.config = {
79 | -- Highlight only in normal buffers (ones with empty 'buftype'). This is
80 | -- useful to not show trailing whitespace where it usually doesn't matter.
81 | only_in_normal_buffers = true,
82 | }
83 | --minidoc_afterlines_end
84 |
85 | -- Module functionality =======================================================
86 | --- Highlight trailing whitespace in current window
87 | MiniTrailspace.highlight = function()
88 | -- Highlight only in normal mode
89 | if H.is_disabled() or vim.fn.mode() ~= 'n' then
90 | MiniTrailspace.unhighlight()
91 | return
92 | end
93 |
94 | -- Possibly work only in normal buffers
95 | if H.get_config().only_in_normal_buffers and not H.is_buffer_normal() then return end
96 |
97 | -- Don't add match id on top of existing one
98 | if H.get_match_id() ~= nil then return end
99 |
100 | vim.fn.matchadd('MiniTrailspace', [[\s\+$]])
101 | end
102 |
103 | --- Unhighlight trailing whitespace in current window
104 | MiniTrailspace.unhighlight = function()
105 | -- Use `pcall` because there is an error if match id is not present. It can
106 | -- happen if something else called `clearmatches`.
107 | pcall(vim.fn.matchdelete, H.get_match_id())
108 | end
109 |
110 | --- Trim trailing whitespace
111 | MiniTrailspace.trim = function()
112 | -- Save cursor position to later restore
113 | local curpos = vim.api.nvim_win_get_cursor(0)
114 | -- Search and replace trailing whitespace
115 | vim.cmd([[keeppatterns %s/\s\+$//e]])
116 | vim.api.nvim_win_set_cursor(0, curpos)
117 | end
118 |
119 | --- Trim last blank lines
120 | MiniTrailspace.trim_last_lines = function()
121 | local n_lines = vim.api.nvim_buf_line_count(0)
122 | local last_nonblank = vim.fn.prevnonblank(n_lines)
123 | if last_nonblank < n_lines then vim.api.nvim_buf_set_lines(0, last_nonblank, n_lines, true, {}) end
124 | end
125 |
126 | -- Helper data ================================================================
127 | -- Module default config
128 | H.default_config = vim.deepcopy(MiniTrailspace.config)
129 |
130 | -- Helper functionality =======================================================
131 | -- Settings -------------------------------------------------------------------
132 | H.setup_config = function(config)
133 | H.check_type('config', config, 'table', true)
134 | config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
135 |
136 | H.check_type('only_in_normal_buffers', config.only_in_normal_buffers, 'boolean')
137 |
138 | return config
139 | end
140 |
141 | H.apply_config = function(config) MiniTrailspace.config = config end
142 |
143 | H.create_autocommands = function(config)
144 | local gr = vim.api.nvim_create_augroup('MiniTrailspace', {})
145 |
146 | local au = function(event, pattern, callback, desc)
147 | vim.api.nvim_create_autocmd(event, { group = gr, pattern = pattern, callback = callback, desc = desc })
148 | end
149 |
150 | -- NOTE: Respecting both `WinEnter` and `BufEnter` seems to be useful to
151 | -- account of different order of handling buffer opening in new window.
152 | -- Notable example: 'nvim-tree' at commit a1600e5.
153 | au({ 'WinEnter', 'BufEnter', 'InsertLeave' }, '*', MiniTrailspace.highlight, 'Highlight')
154 | au({ 'WinLeave', 'BufLeave', 'InsertEnter' }, '*', MiniTrailspace.unhighlight, 'Unhighlight')
155 |
156 | if config.only_in_normal_buffers then
157 | -- Add tracking of 'buftype' changing because it can be set after events on
158 | -- which highlighting is done. If not done, highlighting appears but
159 | -- disappears if buffer is reentered.
160 | au('OptionSet', 'buftype', H.track_normal_buffer, 'Track normal buffer')
161 | end
162 |
163 | au('ColorScheme', '*', H.create_default_hl, 'Ensure colors')
164 | end
165 |
166 | H.create_default_hl = function() vim.api.nvim_set_hl(0, 'MiniTrailspace', { default = true, link = 'Error' }) end
167 |
168 | H.is_disabled = function() return vim.g.minitrailspace_disable == true or vim.b.minitrailspace_disable == true end
169 |
170 | H.get_config = function(config)
171 | return vim.tbl_deep_extend('force', MiniTrailspace.config, vim.b.minitrailspace_config or {}, config or {})
172 | end
173 |
174 | H.track_normal_buffer = function()
175 | if not H.get_config().only_in_normal_buffers then return end
176 |
177 | -- This should be used with 'OptionSet' event for 'buftype' option
178 | -- Empty 'buftype' means "normal buffer"
179 | if vim.v.option_new == '' then
180 | MiniTrailspace.highlight()
181 | else
182 | MiniTrailspace.unhighlight()
183 | end
184 | end
185 |
186 | H.is_buffer_normal = function(buf_id) return vim.bo[buf_id or 0].buftype == '' end
187 |
188 | H.get_match_id = function()
189 | -- NOTE: this can be replaced with more efficient custom tracking of id per
190 | -- window but it will have more edge cases (like won't update on manual
191 | -- `clearmatches()`)
192 | for _, match in ipairs(vim.fn.getmatches()) do
193 | if match.group == 'MiniTrailspace' then return match.id end
194 | end
195 | end
196 |
197 | -- Utilities ------------------------------------------------------------------
198 | H.error = function(msg) error('(mini.trailspace) ' .. msg, 0) end
199 |
200 | H.check_type = function(name, val, ref, allow_nil)
201 | if type(val) == ref or (ref == 'callable' and vim.is_callable(val)) or (allow_nil and val == nil) then return end
202 | H.error(string.format('`%s` should be %s, not %s', name, ref, type(val)))
203 | end
204 |
205 | return MiniTrailspace
206 |
--------------------------------------------------------------------------------