diff --git a/.nvimlog b/.nvimlog new file mode 100644 index 00000000..167af117 --- /dev/null +++ b/.nvimlog @@ -0,0 +1,12 @@ +WRN 2026-02-08T10:26:22.500 ?.251198 server_start:199: Failed to start server: operation not permitted: /run/user/1000/nvim.251198.0 +WRN 2026-02-08T10:27:17.510 ?.252159 server_start:199: Failed to start server: operation not permitted: /run/user/1000/nvim.252159.0 +WRN 2026-02-08T10:27:27.437 ?.252367 server_start:199: Failed to start server: operation not permitted: /run/user/1000/nvim.252367.0 +WRN 2026-02-08T12:08:26.579 ?.359867 server_start:199: Failed to start server: operation not permitted: /run/user/1000/nvim.359867.0 +WRN 2026-02-08T12:10:44.180 ?.364737 server_start:199: Failed to start server: operation not permitted: /run/user/1000/nvim.364737.0 +ERR 2026-02-08T12:10:44.218 ?.364737 pty_proc_spawn:186: forkpty failed: Permission denied +WRN 2026-02-08T12:18:41.628 ?.381522 server_start:199: Failed to start server: operation not permitted: /run/user/1000/nvim.381522.0 +WRN 2026-02-08T16:12:49.348 ?.515879 server_start:199: Failed to start server: operation not permitted: /run/user/1000/nvim.515879.0 +WRN 2026-02-08T16:33:59.951 ?.543624 server_start:199: Failed to start server: operation not permitted: /run/user/1000/nvim.543624.0 +WRN 2026-02-08T16:57:38.954 ?.571806 server_start:199: Failed to start server: operation not permitted: /run/user/1000/nvim.571806.0 +WRN 2026-02-08T19:51:48.962 ?.702813 server_start:199: Failed to start server: operation not permitted: /run/user/1000/nvim.702813.0 +WRN 2026-02-08T19:51:48.968 ?.702844 server_start:199: Failed to start server: operation not permitted: /run/user/1000/nvim.702844.0 diff --git a/init.lua b/init.lua index c214f9c2..d98e3d51 100644 --- a/init.lua +++ b/init.lua @@ -101,8 +101,7 @@ vim.g.have_nerd_font = false -- Make line numbers default vim.o.number = true -- You can also add relative line numbers, to help with jumping. --- Experiment for yourself to see if you like it! --- vim.o.relativenumber = true +vim.o.relativenumber = true -- Enable mouse mode, can be useful for resizing splits for example! vim.o.mouse = 'a' @@ -118,8 +117,13 @@ vim.schedule(function() vim.o.clipboard = 'unnamedplus' end) --- Enable break indent -vim.o.breakindent = true +require('custom.wrapping').setup() + +-- Default to 4-space indentation unless overridden by filetype/plugins +vim.o.tabstop = 4 +vim.o.shiftwidth = 4 +vim.o.softtabstop = 4 +vim.o.expandtab = true -- Save undo history vim.o.undofile = true @@ -191,13 +195,13 @@ vim.keymap.set('t', '', '', { desc = 'Exit terminal mode' } -- vim.keymap.set('n', '', 'echo "Use j to move!!"') -- Keybinds to make split navigation easier. --- Use CTRL+ to switch between windows +-- NOTE: are managed by vim-tmux-navigator in this config. -- -- See `:help wincmd` for a list of all window commands -vim.keymap.set('n', '', '', { desc = 'Move focus to the left window' }) -vim.keymap.set('n', '', '', { desc = 'Move focus to the right window' }) -vim.keymap.set('n', '', '', { desc = 'Move focus to the lower window' }) -vim.keymap.set('n', '', '', { desc = 'Move focus to the upper window' }) +vim.keymap.set('n', 'wh', '', { desc = 'Move focus to the left window' }) +vim.keymap.set('n', 'wl', '', { desc = 'Move focus to the right window' }) +vim.keymap.set('n', 'wj', '', { desc = 'Move focus to the lower window' }) +vim.keymap.set('n', 'wk', '', { desc = 'Move focus to the upper window' }) -- NOTE: Some terminals have colliding keymaps or are not able to send distinct keycodes -- vim.keymap.set("n", "", "H", { desc = "Move window to the left" }) @@ -219,6 +223,60 @@ vim.api.nvim_create_autocmd('TextYankPost', { end, }) +local path_on_save_group = vim.api.nvim_create_augroup('kickstart-create-path-on-save', { clear = true }) + +-- Only create paths inside these roots (defaults to current working directory). +-- Add more roots as needed, e.g. vim.fn.expand '~/notes' +local path_create_roots = { + vim.fn.getcwd(), +} + +local is_in_allowed_root = function(path) + local abs_path = vim.fn.fnamemodify(path, ':p') + for _, root in ipairs(path_create_roots) do + local abs_root = vim.fn.fnamemodify(root, ':p') + if abs_path:sub(1, #abs_root) == abs_root then + return true + end + end + return false +end + +local ensure_owner_rwx = function(path) + local perms = vim.fn.getfperm(path) + if perms ~= '' and perms:sub(1, 3) ~= 'rwx' then + vim.fn.setfperm(path, 'rwx' .. perms:sub(4)) + end +end + +vim.api.nvim_create_autocmd('BufWritePre', { + desc = 'Create missing parent directories on save', + group = path_on_save_group, + callback = function(args) + if vim.bo[args.buf].buftype ~= '' then + return + end + + local file_path = vim.api.nvim_buf_get_name(args.buf) + if file_path == '' or file_path:match '^%w+://' then + return + end + + local uv = vim.uv or vim.loop + local abs_path = vim.fn.fnamemodify(file_path, ':p') + if not is_in_allowed_root(abs_path) then + return + end + + local parent = vim.fn.fnamemodify(abs_path, ':h') + + if uv.fs_stat(parent) == nil then + vim.fn.mkdir(parent, 'p', '0700') + end + ensure_owner_rwx(parent) + end, +}) + -- [[ Install `lazy.nvim` plugin manager ]] -- See `:help lazy.nvim.txt` or https://github.com/folke/lazy.nvim for more info local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim' @@ -665,19 +723,44 @@ require('lazy').setup({ -- - settings (table): Override the default settings passed when initializing the server. -- For example, to see the options for `lua_ls`, you could go to: https://luals.github.io/wiki/settings/ local servers = { - -- clangd = {}, - -- gopls = {}, - -- pyright = {}, - -- rust_analyzer = {}, - -- ... etc. See `:help lspconfig-all` for a list of all the pre-configured LSPs - -- - -- Some languages (like typescript) have entire language plugins that can be useful: - -- https://github.com/pmizio/typescript-tools.nvim - -- - -- But for many setups, the LSP (`ts_ls`) will work just fine - -- ts_ls = {}, - -- - + -- Languages + clangd = {}, + gopls = { + settings = { + gopls = { + analyses = { + unusedparams = true, + shadow = true, + }, + staticcheck = true, + gofumpt = true, + }, + }, + }, + pyright = { + settings = { + python = { + analysis = { + typeCheckingMode = 'basic', + autoSearchPaths = true, + useLibraryCodeForTypes = true, + }, + }, + }, + }, + ruff = {}, + rust_analyzer = {}, + bashls = {}, + awk_ls = {}, + cssls = {}, + htmx = {}, + html = {}, + jsonls = {}, + yamlls = {}, + taplo = {}, + elixirls = {}, + gh_actions_ls = {}, + jqls = {}, lua_ls = { -- cmd = { ... }, -- filetypes = { ... }, @@ -692,6 +775,25 @@ require('lazy').setup({ }, }, }, + -- Tools + eslint = {}, + astro = {}, + tailwindcss = {}, + docker_language_server = {}, + docker_compose_language_service = {}, + marksman = {}, + postgres_lsp = {}, + neocmake = {}, + buf_ls = {}, + + -- ... etc. See `:help lspconfig-all` for a list of all the pre-configured LSPs + -- + -- Some languages (like typescript) have entire language plugins that can be useful: + -- https://github.com/pmizio/typescript-tools.nvim + -- + -- But for many setups, the LSP (`ts_ls`) will work just fine + -- ts_ls = {}, + -- } ---@type MasonLspconfigSettings ---@diagnostic disable-next-line: missing-fields @@ -715,6 +817,7 @@ require('lazy').setup({ local ensure_installed = vim.tbl_keys(servers or {}) vim.list_extend(ensure_installed, { 'stylua', -- Used to format Lua code + 'markdownlint', -- Used by nvim-lint for Markdown buffers }) require('mason-tool-installer').setup { ensure_installed = ensure_installed } @@ -933,22 +1036,36 @@ require('lazy').setup({ }, { -- Highlight, edit, and navigate code 'nvim-treesitter/nvim-treesitter', + lazy = false, build = ':TSUpdate', - main = 'nvim-treesitter.configs', -- Sets main module to use for opts -- [[ Configure Treesitter ]] See `:help nvim-treesitter` opts = { ensure_installed = { 'bash', 'c', 'diff', 'html', 'lua', 'luadoc', 'markdown', 'markdown_inline', 'query', 'vim', 'vimdoc' }, - -- Autoinstall languages that are not installed auto_install = true, - highlight = { - enable = true, - -- Some languages depend on vim's regex highlighting system (such as Ruby) for indent rules. - -- If you are experiencing weird indenting issues, add the language to - -- the list of additional_vim_regex_highlighting and disabled languages for indent. - additional_vim_regex_highlighting = { 'ruby' }, - }, - indent = { enable = true, disable = { 'ruby' } }, }, + config = function(_, opts) + local ts = require 'nvim-treesitter' + ts.setup() + + -- Install only parsers that are not already present to avoid repeated startup spam. + if opts.auto_install and opts.ensure_installed and #opts.ensure_installed > 0 then + local installed = ts.get_installed 'parsers' + local missing = vim.tbl_filter(function(lang) + return not vim.list_contains(installed, lang) + end, opts.ensure_installed) + + if #missing > 0 then + ts.install(missing) + end + end + + vim.api.nvim_create_autocmd('FileType', { + group = vim.api.nvim_create_augroup('kickstart-treesitter', { clear = true }), + callback = function() + pcall(vim.treesitter.start) + end, + }) + end, -- There are additional nvim-treesitter modules that you can use to interact -- with nvim-treesitter. You should go explore a few and see what interests you: -- @@ -961,23 +1078,11 @@ require('lazy').setup({ -- init.lua. If you want these files, they are in the repository, so you can just download them and -- place them in the correct locations. - -- NOTE: Next step on your Neovim journey: Add/Configure additional plugins for Kickstart - -- - -- Here are some example plugins that I've included in the Kickstart repository. - -- Uncomment any of the lines below to enable them (you will need to restart nvim). - -- - -- require 'kickstart.plugins.debug', - -- require 'kickstart.plugins.indent_line', - -- require 'kickstart.plugins.lint', - -- require 'kickstart.plugins.autopairs', - -- require 'kickstart.plugins.neo-tree', - -- require 'kickstart.plugins.gitsigns', -- adds gitsigns recommend keymaps - -- NOTE: The import below can automatically add your own plugins, configuration, etc from `lua/custom/plugins/*.lua` -- This is the easiest way to modularize your config. -- -- Uncomment the following line and add your plugins to `lua/custom/plugins/*.lua` to get going. - -- { import = 'custom.plugins' }, + { import = 'custom.plugins' }, -- -- For additional information with loading, sourcing and examples see `:help lazy.nvim-🔌-plugin-spec` -- Or use telescope! diff --git a/lua/kickstart/health.lua b/lua/custom/health.lua similarity index 100% rename from lua/kickstart/health.lua rename to lua/custom/health.lua diff --git a/lua/kickstart/plugins/autopairs.lua b/lua/custom/plugins/autopairs.lua similarity index 100% rename from lua/kickstart/plugins/autopairs.lua rename to lua/custom/plugins/autopairs.lua diff --git a/lua/kickstart/plugins/debug.lua b/lua/custom/plugins/debug.lua similarity index 100% rename from lua/kickstart/plugins/debug.lua rename to lua/custom/plugins/debug.lua diff --git a/lua/kickstart/plugins/gitsigns.lua b/lua/custom/plugins/gitsigns.lua similarity index 100% rename from lua/kickstart/plugins/gitsigns.lua rename to lua/custom/plugins/gitsigns.lua diff --git a/lua/custom/plugins/harpoon.lua b/lua/custom/plugins/harpoon.lua new file mode 100644 index 00000000..21ec13d6 --- /dev/null +++ b/lua/custom/plugins/harpoon.lua @@ -0,0 +1,72 @@ +return { + 'ThePrimeagen/harpoon', + branch = 'harpoon2', + dependencies = { 'nvim-lua/plenary.nvim' }, + config = function() + local harpoon = require 'harpoon' + harpoon:setup { + default = { + select = function(list_item, _, options) + if not list_item or not list_item.value or list_item.value == '' then + return + end + + options = options or {} + local open_cmd = 'edit' + if options.vsplit then + open_cmd = 'vsplit' + elseif options.split then + open_cmd = 'split' + elseif options.tabedit then + open_cmd = 'tabedit' + end + + vim.cmd(open_cmd .. ' ' .. vim.fn.fnameescape(list_item.value)) + + local context = list_item.context or {} + local row = math.max(1, tonumber(context.row) or 1) + local col = math.max(0, tonumber(context.col) or 0) + local line_count = vim.api.nvim_buf_line_count(0) + if line_count < 1 then + return + end + + if row > line_count then + row = line_count + end + + local row_text = vim.api.nvim_buf_get_lines(0, row - 1, row, false)[1] or '' + if col > #row_text then + col = #row_text + end + + pcall(vim.api.nvim_win_set_cursor, 0, { row, col }) + end, + }, + } + + vim.keymap.set('n', 'a', function() + if vim.bo.buftype ~= '' then + vim.notify('Harpoon: current buffer is not a file', vim.log.levels.INFO) + return + end + harpoon:list():add() + end, { desc = 'Harpoon add' }) + vim.keymap.set('n', 'hm', function() + harpoon.ui:toggle_quick_menu(harpoon:list()) + end, { desc = 'Harpoon menu' }) + vim.keymap.set('n', '', function() + harpoon.ui:toggle_quick_menu(harpoon:list()) + end, { desc = 'Harpoon menu' }) + vim.api.nvim_create_user_command('HarpoonMenu', function() + harpoon.ui:toggle_quick_menu(harpoon:list()) + end, { desc = 'Toggle Harpoon quick menu' }) + + vim.keymap.set('n', '', function() + harpoon:list():prev() + end) + vim.keymap.set('n', '', function() + harpoon:list():next() + end) + end, +} diff --git a/lua/kickstart/plugins/indent_line.lua b/lua/custom/plugins/indent_line.lua similarity index 100% rename from lua/kickstart/plugins/indent_line.lua rename to lua/custom/plugins/indent_line.lua diff --git a/lua/kickstart/plugins/lint.lua b/lua/custom/plugins/lint.lua similarity index 88% rename from lua/kickstart/plugins/lint.lua rename to lua/custom/plugins/lint.lua index dec42f09..61ec2d27 100644 --- a/lua/kickstart/plugins/lint.lua +++ b/lua/custom/plugins/lint.lua @@ -8,6 +8,15 @@ return { lint.linters_by_ft = { markdown = { 'markdownlint' }, } + lint.linters.markdownlint = vim.tbl_deep_extend('force', lint.linters.markdownlint or {}, { + args = { + '--stdin', + '--disable', + 'MD013', -- line length + 'MD033', -- inline HTML + 'MD041', -- first line heading + }, + }) -- To allow other plugins to add linters to require('lint').linters_by_ft, -- instead set linters_by_ft like this: diff --git a/lua/custom/plugins/markdown.lua b/lua/custom/plugins/markdown.lua new file mode 100644 index 00000000..647ab76a --- /dev/null +++ b/lua/custom/plugins/markdown.lua @@ -0,0 +1,27 @@ +return { + { + 'MeanderingProgrammer/render-markdown.nvim', + ft = { 'markdown' }, + dependencies = { 'nvim-treesitter/nvim-treesitter', 'nvim-tree/nvim-web-devicons' }, + opts = { + file_types = { 'markdown' }, + }, + }, + { + 'iamcco/markdown-preview.nvim', + ft = { 'markdown' }, + cmd = { 'MarkdownPreviewToggle', 'MarkdownPreview', 'MarkdownPreviewStop' }, + build = function() + vim.fn['mkdp#util#install']() + end, + init = function() + vim.g.mkdp_auto_start = 0 + vim.g.mkdp_auto_close = 1 + vim.g.mkdp_refresh_slow = 0 + vim.g.mkdp_filetypes = { 'markdown' } + end, + keys = { + { 'mp', 'MarkdownPreviewToggle', desc = '[M]arkdown [P]review' }, + }, + }, +} diff --git a/lua/kickstart/plugins/neo-tree.lua b/lua/custom/plugins/neo-tree.lua similarity index 100% rename from lua/kickstart/plugins/neo-tree.lua rename to lua/custom/plugins/neo-tree.lua diff --git a/lua/custom/plugins/neogit.lua b/lua/custom/plugins/neogit.lua new file mode 100644 index 00000000..8a4fb9d6 --- /dev/null +++ b/lua/custom/plugins/neogit.lua @@ -0,0 +1,18 @@ +return { + 'NeogitOrg/neogit', + lazy = true, + dependencies = { + 'nvim-lua/plenary.nvim', -- required + 'sindrets/diffview.nvim', -- optional - Diff integration + + -- Only one of these is needed. + 'nvim-telescope/telescope.nvim', -- optional + 'ibhagwan/fzf-lua', -- optional + 'nvim-mini/mini.pick', -- optional + 'folke/snacks.nvim', -- optional + }, + cmd = 'Neogit', + keys = { + { 'gg', 'Neogit', desc = 'Show Neogit UI' }, + }, +} diff --git a/lua/custom/plugins/opencode.lua b/lua/custom/plugins/opencode.lua new file mode 100644 index 00000000..60bd2500 --- /dev/null +++ b/lua/custom/plugins/opencode.lua @@ -0,0 +1,83 @@ +return { + 'NickvanDyke/opencode.nvim', + dependencies = { + -- Recommended for `ask()` and `select()`. + -- Required for `snacks` provider. + ---@module 'snacks' <- Loads `snacks.nvim` types for configuration intellisense. + { 'folke/snacks.nvim', opts = { input = {}, picker = {}, terminal = {} } }, + }, + config = function() + ---@type opencode.Opts + vim.g.opencode_opts = { + provider = { + enabled = 'snacks', + snacks = { + auto_close = false, + win = { + position = 'right', + border = 'rounded', + width = math.floor(vim.o.columns * 0.35), + enter = false, + }, + }, + }, + } + + -- Required for `opts.events.reload`. + vim.o.autoread = true + + -- Recommended/example keymaps. + vim.keymap.set({ 'n', 'x' }, '', function() require('opencode').ask('@this: ', { submit = true }) end, { desc = 'Ask opencode…' }) + vim.keymap.set({ 'n', 'x' }, '', function() require('opencode').select() end, { desc = 'Execute opencode action…' }) + local function opencode_toggle() + local ok, err = pcall(function() + require('opencode').toggle() + end) + if not ok then + vim.notify('opencode toggle failed: ' .. tostring(err), vim.log.levels.ERROR) + end + end + + vim.keymap.set({ 'n', 't' }, '', opencode_toggle, { desc = 'Toggle opencode' }) + vim.keymap.set('n', 'oc', opencode_toggle, { desc = 'Toggle opencode chat' }) + + local opencode_term_nav_group = vim.api.nvim_create_augroup('opencode-term-nav', { clear = true }) + vim.api.nvim_create_autocmd('FileType', { + group = opencode_term_nav_group, + pattern = 'opencode_terminal', + callback = function(ev) + local function tmux_nav(cmd) + local esc = vim.api.nvim_replace_termcodes('', true, false, true) + vim.api.nvim_feedkeys(esc, 'n', false) + vim.cmd(cmd) + end + + vim.keymap.set('t', '', function() + tmux_nav 'TmuxNavigateLeft' + end, { buffer = ev.buf, silent = true, desc = 'Tmux navigate left from opencode' }) + + vim.keymap.set('t', '', function() + tmux_nav 'TmuxNavigateDown' + end, { buffer = ev.buf, silent = true, desc = 'Tmux navigate down from opencode' }) + + vim.keymap.set('t', '', function() + tmux_nav 'TmuxNavigateUp' + end, { buffer = ev.buf, silent = true, desc = 'Tmux navigate up from opencode' }) + + vim.keymap.set('t', '', function() + tmux_nav 'TmuxNavigateRight' + end, { buffer = ev.buf, silent = true, desc = 'Tmux navigate right from opencode' }) + end, + }) + + vim.keymap.set({ 'n', 'x' }, 'go', function() return require('opencode').operator '@this ' end, { desc = 'Add range to opencode', expr = true }) + vim.keymap.set('n', 'goo', function() return require('opencode').operator '@this ' .. '_' end, { desc = 'Add line to opencode', expr = true }) + + vim.keymap.set('n', '', function() require('opencode').command 'session.half.page.up' end, { desc = 'Scroll opencode up' }) + vim.keymap.set('n', '', function() require('opencode').command 'session.half.page.down' end, { desc = 'Scroll opencode down' }) + + -- You may want these if you stick with the opinionated "" and "" above — otherwise consider "o…". + vim.keymap.set('n', '+', '', { desc = 'Increment under cursor', noremap = true }) + vim.keymap.set('n', '-', '', { desc = 'Decrement under cursor', noremap = true }) + end, +} diff --git a/lua/custom/plugins/supermaven.lua b/lua/custom/plugins/supermaven.lua new file mode 100644 index 00000000..b16e65ad --- /dev/null +++ b/lua/custom/plugins/supermaven.lua @@ -0,0 +1,21 @@ +return { + 'supermaven-inc/supermaven-nvim', + config = function() + require('supermaven-nvim').setup { + keymaps = { + accept_suggestion = '', + clear_suggestion = '', + accept_word = '', + }, + ignore_filetypes = { cpp = true }, -- or { "cpp", } + color = { + suggestion_color = '#ffffff', + cterm = 244, + }, + log_level = 'info', -- set to "off" to disable logging completely + disable_inline_completion = false, -- disables inline completion for use with cmp + disable_keymaps = false, -- disables built in keymaps for more manual control + condition = function() return false end, -- condition to check for stopping supermaven, `true` means to stop supermaven when the condition is true. + } + end, +} diff --git a/lua/custom/plugins/tmux_navigator.lua b/lua/custom/plugins/tmux_navigator.lua new file mode 100644 index 00000000..26fb72ba --- /dev/null +++ b/lua/custom/plugins/tmux_navigator.lua @@ -0,0 +1,18 @@ +return { + 'christoomey/vim-tmux-navigator', + cmd = { + 'TmuxNavigateLeft', + 'TmuxNavigateDown', + 'TmuxNavigateUp', + 'TmuxNavigateRight', + 'TmuxNavigatePrevious', + 'TmuxNavigatorProcessList', + }, + keys = { + { '', 'TmuxNavigateLeft' }, + { '', 'TmuxNavigateDown' }, + { '', 'TmuxNavigateUp' }, + { '', 'TmuxNavigateRight' }, + { '', 'TmuxNavigatePrevious' }, + }, +} diff --git a/lua/custom/plugins/typescript_tools.lua b/lua/custom/plugins/typescript_tools.lua new file mode 100644 index 00000000..ba7fb204 --- /dev/null +++ b/lua/custom/plugins/typescript_tools.lua @@ -0,0 +1,5 @@ +return { + 'pmizio/typescript-tools.nvim', + dependencies = { 'nvim-lua/plenary.nvim', 'neovim/nvim-lspconfig' }, + opts = {}, +} diff --git a/lua/custom/wrapping.lua b/lua/custom/wrapping.lua new file mode 100644 index 00000000..3732a8c0 --- /dev/null +++ b/lua/custom/wrapping.lua @@ -0,0 +1,81 @@ +local M = {} + +local defaults = { + width = 100, + patterns = { '*.md', '*.markdown' }, +} + +local function apply_wrap_options(local_opts, width) + local_opts.wrap = true + local_opts.linebreak = true + local_opts.breakindent = true + local_opts.textwidth = width + local_opts.colorcolumn = '' + local_opts.formatoptions:append 't' + local_opts.formatoptions:append 'a' + local_opts.formatoptions:remove 'l' +end + +local function can_reflow(bufnr) + return vim.api.nvim_buf_is_valid(bufnr) and vim.bo[bufnr].modifiable and vim.bo[bufnr].buftype == '' +end + +local function reflow_whole_buffer(bufnr, preserve_view) + if not can_reflow(bufnr) then + return + end + + vim.api.nvim_buf_call(bufnr, function() + local view = preserve_view and vim.fn.winsaveview() or nil + vim.cmd 'silent keepjumps normal! gggqG' + if view then + vim.fn.winrestview(view) + end + end) +end + +function M.setup(opts) + opts = vim.tbl_deep_extend('force', defaults, opts or {}) + + apply_wrap_options(vim.opt, opts.width) + + local group = vim.api.nvim_create_augroup('custom_wrapping_rules', { clear = true }) + + vim.api.nvim_create_autocmd('FileType', { + group = group, + callback = function() + -- Apply after ftplugins so local overrides don't drop wrap options. + vim.schedule(function() + apply_wrap_options(vim.opt_local, opts.width) + end) + end, + }) + + vim.api.nvim_create_autocmd('BufReadPost', { + group = group, + pattern = opts.patterns, + callback = function(args) + -- Reflow existing prose on open so the hard wrap rule is applied immediately. + if vim.b[args.buf].did_initial_wrap then + return + end + vim.b[args.buf].did_initial_wrap = true + vim.schedule(function() + reflow_whole_buffer(args.buf, false) + end) + end, + }) + + vim.api.nvim_create_autocmd('BufWritePre', { + group = group, + pattern = opts.patterns, + callback = function(args) + if not vim.bo[args.buf].modified then + return + end + reflow_whole_buffer(args.buf, true) + end, + }) +end + +return M