commit b8dfde353aacce8e4777b1972ed86f377d29f386 Author: Stephen Waits Date: Mon Oct 30 22:04:32 2023 -0600 initial commit diff --git a/.stylua.toml b/.stylua.toml new file mode 100644 index 0000000..2278abd --- /dev/null +++ b/.stylua.toml @@ -0,0 +1,3 @@ +indent_type = "Spaces" +indent_width = 2 +column_width = 100 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2fad05a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright © 2023 Stephen Waits + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f8037b4 --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +# Neovim Scratch Buffer Plugin + +This plugin provides a simple way to work with scratch buffers in Neovim. It +allows you to quickly open a scratch buffer in your current window or in a new +split window. The plugin also offers the flexibility to configure the default +name of the scratch buffer. + +This plugin is based on +[vim-scratch](https://github.com/duff/vim-scratch/tree/master), but written in +lua for Neovim. + +## Features + +- Open a scratch buffer in the current window. +- Open a scratch buffer in a new split window. +- Automatically switch to an existing scratch buffer if it's already open. +- Configure the default name for the scratch buffer. +- The scratch buffer acts as a temporary workspace and is not backed by a file. + +## Installation + +To install this plugin, you can use your favorite Neovim package manager. For example: + +### [lazy](https://github.com/folke/lazy.nvim) (recommended) + +```lua +{ + "https://git.sr.ht/~swaits/scratch.nvim", + lazy = true, + keys = { + { "bs", "Scratch", desc = "Scratch Buffer", mode = "n" }, + { "bS", "ScratchSplit", desc = "Scratch Buffer (split)", mode = "n" }, + }, + cmd = { + "Scratch", + "ScratchSplit", + }, + opts = {}, +} +``` + +### [pckr](https://github.com/lewis6991/pckr.nvim) + +```lua +{ + "https://git.sr.ht/~swaits/scratch.nvim", + config = function() + require("scratch").setup() + end +} +``` + +### [vim-plug](https://github.com/junegunn/vim-plug) + +```vim +Plug 'https://git.sr.ht/~swaits/scratch.nvim' +lua require("scratch").setup() +``` + +### Configuring + +The default configuration options are listed below: + +```lua +opts = { + -- The name of the scratch buffer + buffer_name = "_SCRATCH_", +} +``` + +## Usage + +### Commands + +The plugin provides two commands: + +- `:Scratch` — Opens or switches to the scratch buffer in the current window. +- `:ScratchSplit` — Opens or switches to the scratch buffer in a new split window. + +### Lua Functions + +You can also use the plugin's Lua functions directly: + +- `require('scratch').open()` — Equivalent to `:Scratch`. +- `require('scratch').split()` — Equivalent to `:ScratchSplit`. + +## License + +[MIT License](LICENSE) + +## Contributing + +Contributions are welcome! Feel free to open issues or submit pull requests. diff --git a/lua/scratch/init.lua b/lua/scratch/init.lua new file mode 100644 index 0000000..2fc94d3 --- /dev/null +++ b/lua/scratch/init.lua @@ -0,0 +1,166 @@ +local M = {} + +--- Checks if the current buffer is modified. +-- +-- Returns: +-- bool: True if the buffer is modified, False otherwise. +-- +local function is_buffer_modified() + local buf = vim.api.nvim_get_current_buf() + return vim.api.nvim_buf_get_option(buf, "modified") +end + +--- Switch to an existing buffer in Neovim. +-- +-- This function checks if a buffer is already open in a window. If it is, it +-- switches to that window. If it is not, it switches to the existing buffer. +-- +-- @param new_window (boolean) Whether to open the buffer in a new window. If +-- true, the buffer will be opened in a new split window. If false, the buffer +-- will be opened in the current window. +-- @param buffer_num (number) The buffer number of the buffer. +-- +-- @return None +-- +local function switch_to_existing_buffer(new_window, buffer_num) + -- Check if the buffer number is valid + if type(buffer_num) ~= "number" or buffer_num < 1 then + error("Invalid buffer number provided to switch_to_existing_buffer") + return + end + + -- See if this buffer is already open in a window + local window_num = vim.fn.bufwinnr(buffer_num) + + if window_num ~= -1 then + -- Switch to that window (even if we're already there) + vim.cmd(window_num .. " wincmd w") + else + -- Switch to the existing scratch buffer + local cmd = new_window and "split +buffer " or "buffer " + vim.cmd(cmd .. buffer_num) + end +end + +--- Creates a new scratch buffer in Neovim. +-- +-- This function creates a new scratch buffer in Neovim, either in a new window +-- or by editing the current buffer. The buffer is configured to work like a +-- scratch buffer, meaning it is not backed by a file and has specific options +-- set. +-- +-- @param new_window (boolean) Whether to create the scratch buffer in a new +-- window. If true, the buffer will be created in a new window. If false, the +-- buffer will be created by editing the current buffer. +-- @param buffer_name (string) The name of the buffer. +-- +-- @return None +-- +local function create_new_scratch_buffer(new_window, buffer_name) + -- Check if the buffer name is set + if not buffer_name or type(buffer_name) ~= "string" then + error("Buffer name not set or invalid in create_new_scratch_buffer") + return + end + + -- Either new (for split) or edit the new buffer with buffer_name + local cmd = new_window and "new " or "edit " + vim.cmd(cmd .. buffer_name) + + -- Sets the options for the buffer to work like a scratch buffer. + local buf = vim.api.nvim_get_current_buf() -- should always be 0 + vim.api.nvim_buf_set_option(buf, "bufhidden", "hide") -- what to do when the buffer is hidden + vim.api.nvim_buf_set_option(buf, "buflisted", true) -- include the buffer in the :bnext list + vim.api.nvim_buf_set_option(buf, "buftype", "nofile") -- nofile means the buffer isn't backed by a file and we control its name + vim.api.nvim_buf_set_option(buf, "swapfile", false) -- never swapfiles +end + +--- Opens or splits a window based on the specified conditions. +-- +-- This function checks if a new window needs to be opened or if it should +-- switch to an existing buffer. It first determines if a new window is +-- required based on the input parameter or if the current buffer is modified. +-- Then, it searches for an existing scratch buffer. If found, it switches to +-- that buffer. Otherwise, it creates a new scratch buffer. +-- +-- @param new_window A boolean indicating whether a new window should be created. If not provided, +-- it defaults to the state of the buffer's modification (true if modified). +-- +-- @return None +-- +local function open_or_split(new_window) + -- Create new window if requested or if buffer is modified + new_window = new_window or is_buffer_modified() + + -- Look for an existing scratch buffer + local scratch_buffer_num = vim.fn.bufnr(M.config.buffer_name) + if scratch_buffer_num ~= -1 then + -- Found it, switch to it + switch_to_existing_buffer(new_window, scratch_buffer_num) + else + -- Didn't find it, create one + create_new_scratch_buffer(new_window, M.config.buffer_name) + end +end + +--- Splits the current window and opens or switches to the scratch buffer. +-- +-- This function is designed to be a public interface for opening or switching +-- to the scratch buffer in a new split window. It internally calls the +-- `open_or_split` function with `true` to indicate the need for a split window. +-- +-- Usage: +-- :lua require('scratch').split() +-- +function M.split() + open_or_split(true) +end + +--- Opens or switches to the scratch buffer in the current window. +-- +-- This function is a public interface for opening or switching to the scratch +-- buffer in the current window without splitting. It calls the `open_or_split` +-- function with `false`, indicating no new window is needed. +-- +-- Usage: +-- :lua require('scratch').open() +-- +function M.open() + open_or_split(false) +end + +--- Sets up the scratch buffer plugin with optional configuration. +-- +-- This function initializes the scratch buffer plugin and binds Neovim +-- commands to the plugin's functionalities. It also allows for optional +-- configuration through the `opts` parameter. +-- +-- @param opts (table) Optional configuration table. It can contain a key +-- `buffer_name` to specify the name of the scratch buffer. If not provided, +-- defaults to "_SCRATCH_". An error is raised if `buffer_name` is provided but +-- is not a string. +-- +-- Usage: +-- :lua require('scratch').setup({ buffer_name = "my_scratch" }) +-- +function M.setup(opts) + -- Default configuration + M.config = { + buffer_name = "_SCRATCH_", + } + + -- Check for configuration overrides passed in via opts + if opts ~= nil and type(opts) == "table" then + if opts.buffer_name and type(opts.buffer_name) ~= "string" then + error("Invalid buffer name provided in setup options") + return + end + M.config.buffer_name = opts.buffer_name or M.config.buffer_name + end + + -- Bind commands to our lua functions + vim.api.nvim_create_user_command("Scratch", M.open, {}) + vim.api.nvim_create_user_command("ScratchSplit", M.split, {}) +end + +return M