diff --git a/README.md b/README.md index 2b8ac31..087e697 100644 --- a/README.md +++ b/README.md @@ -435,6 +435,14 @@ revision via `jj diffedit`. Immutable revisions show an error on write. -- When true, automatically enter insert mode only if the description is empty. -- If a description already exists, stay in normal mode. auto_insert = false, + + -- Configure the describe/commit editor buffer window + window = { + type = "hsplit", -- Type of window (hsplit/vsplit/floating/tab) + split_size = 0.5, -- Size % of split (height for hsplit, width for vsplit) + floating_width = 0.99, -- Width % for floating window (0.1 to 1.0) + floating_height = 0.95, -- Height % for floating window (0.1 to 1.0) + }, }, -- Customize syntax highlighting colors for the describe buffer @@ -646,6 +654,29 @@ require("jj").setup({ }) ``` +#### `editor.window` + +Control where the describe/commit editor buffer opens: + +- `type`: `"hsplit" | "vsplit" | "floating" | "tab"` +- `split_size`: split ratio for `hsplit`/`vsplit` (default `0.5`) +- `floating_width`: width ratio for floating windows (default `0.99`) +- `floating_height`: height ratio for floating windows (default `0.95`) + +Example: + +```lua +require("jj").setup({ + editor = { + window = { + type = "floating", + floating_width = 0.9, + floating_height = 0.8, + }, + }, +}) +``` + ### Highlight Customization The `highlights` option allows you to customize the colors used in the describe buffer's file status display. Each highlight accepts standard Neovim highlight attributes: diff --git a/lua/jj/core/buffer.lua b/lua/jj/core/buffer.lua index 1f9273e..b5da1d8 100644 --- a/lua/jj/core/buffer.lua +++ b/lua/jj/core/buffer.lua @@ -467,4 +467,21 @@ function M.set_cursor(buf, pos, opts) end end +--- Given an input string resolves the split direction +--- @param type? "hsplit"|"vsplit"|"floating"|"tab" Type of window the terminal is displayed in +--- @return string split_type One of "horizontal", "vertical", "floating", "tab" +function M.resolve_split(type) + if type == "hsplit" then + return "horizontal" + elseif type == "vsplit" then + return "vertical" + elseif type == "floating" then + return "floating" + elseif type == "tab" then + return "tab" + else + return "horizontal" -- default to horizontal if not specified or unrecognized + end +end + return M diff --git a/lua/jj/diff/init.lua b/lua/jj/diff/init.lua index 32595ec..fa06357 100644 --- a/lua/jj/diff/init.lua +++ b/lua/jj/diff/init.lua @@ -38,10 +38,7 @@ local utils = require("jj.utils") ---@field rev string the revision to diff against ---@type jj.diff.config -M.config = { - backend = "native", - backends = {}, -} +M.config = {} ---@type table local backends = {} diff --git a/lua/jj/init.lua b/lua/jj/init.lua index db5ea12..21bf382 100644 --- a/lua/jj/init.lua +++ b/lua/jj/init.lua @@ -27,6 +27,12 @@ M.config = { }, editor = { auto_insert = false, + window = { + type = "hsplit", + split_size = 0.5, + floating_width = 0.99, + floating_height = 0.95, + }, }, highlights = { editor = { @@ -38,8 +44,12 @@ M.config = { }, }, terminal = { + cursor_render_delay = 10, window = { type = "hsplit", + split_size = 0.5, + floating_width = 0.99, + floating_height = 0.95, }, }, diff = { @@ -55,12 +65,9 @@ function M.setup(opts) -- Setup for sub-modules picker.setup(M.config.picker) - editor.setup({ - highlights = M.config.highlights.editor, - auto_insert = M.config.editor.auto_insert, - }) - cmd.setup(M.config.cmd or {}) - terminal.setup(M.config.terminal or {}) + editor.setup(M.config.editor) + cmd.setup(M.config.cmd) + terminal.setup(M.config.terminal) diff.setup(M.config.diff) -- Register the commands form the different modules diff --git a/lua/jj/ui/editor.lua b/lua/jj/ui/editor.lua index 8370689..259e5b4 100644 --- a/lua/jj/ui/editor.lua +++ b/lua/jj/ui/editor.lua @@ -3,67 +3,70 @@ local M = {} local buffer = require("jj.core.buffer") +--- @class jj.ui.editor.opts +--- @field auto_insert? boolean Smart insert: enter insert mode when description is empty, stay in normal mode when one exists +--- @field highlights? jj.ui.editor.highlights +--- @field window? jj.ui.editor.window + --- @class jj.ui.editor.highlights ---@field added? table Highlight settings for added lines ---@field modified? table Highlight settings for modified lines ---@field deleted? table Highlight settings for deleted lines ---@field renamed? table Highlight settings for renamed lines ---- @class jj.ui.editor.opts ----@field auto_insert? boolean Smart insert: enter insert mode when description is empty, stay in normal mode when one exists +--- @class jj.ui.editor.window +--- @field type? "hsplit"|"vsplit"|"floating"|"tab" Type of window the terminal is displayed in +--- @field split_size? number Size % of the split window, either height (hsplit) or width (vsplit) (between 0.1 and 1.0) +--- @field floating_width? number Width % of the floating window (between 0.1 and 1.0) +--- @field floating_height? number Height % of the floating window (between 0.1 and 1.0) -M.highlights = { - -- Only init this one by default since it's not handled natively by neovim - renamed = { fg = "#d29922", ctermfg = "Yellow" }, +--- @type jj.ui.editor.opts +M.opts = { + highlights = { + -- Only init this one by default since it's not handled natively by neovim + renamed = { fg = "#d29922", ctermfg = "Yellow" }, + }, + auto_insert = true, } -M.highlights_initialized = false -M.auto_insert = true + +--- Private module state to track highlights initialization +local highlights_initialized = false -- Initialize highlight groups once local function init_highlights() - if M.highlights_initialized then + if highlights_initialized then return end -- Override the highlight groups if user provided custom settings - if M.highlights.added then - vim.api.nvim_set_hl(0, "Added", M.highlights.added) + if M.opts.highlights.added then + vim.api.nvim_set_hl(0, "Added", M.opts.highlights.added) end - if M.highlights.modified then - vim.api.nvim_set_hl(0, "Changed", M.highlights.modified) + if M.opts.highlights.modified then + vim.api.nvim_set_hl(0, "Changed", M.opts.highlights.modified) end - if M.highlights.deleted then - vim.api.nvim_set_hl(0, "Removed", M.highlights.deleted) + if M.opts.highlights.deleted then + vim.api.nvim_set_hl(0, "Removed", M.opts.highlights.deleted) end -- this one will always be executed since the default nvim highlight group does not exist for renames - if M.highlights.renamed then - vim.api.nvim_set_hl(0, "jjRenamed", M.highlights.renamed) + if M.opts.highlights.renamed then + vim.api.nvim_set_hl(0, "jjRenamed", M.opts.highlights.renamed) end - M.highlights_initialized = true + highlights_initialized = true end --- Setup function to configure highlights and other options ----@param opts? { highlights: jj.ui.editor.highlights, auto_insert?: boolean } Configuration options -function M.setup(opts) - opts = opts or {} - - -- Merge user highlights with defaults - if opts.highlights then - M.highlights = vim.tbl_deep_extend("force", M.highlights, opts.highlights) - end - - -- Store auto_insert option - if opts.auto_insert ~= nil then - M.auto_insert = opts.auto_insert - end +---@param user_opts? jj.ui.editor.opts Configuration options +function M.setup(user_opts) + M.opts = vim.tbl_deep_extend("force", M.opts, user_opts or {}) -- Reset highlights flag to force re-initialization with new highlights - if M.highlights_initialized then - M.highlights_initialized = false + if highlights_initialized then + highlights_initialized = false init_highlights() end end @@ -115,27 +118,18 @@ function M.open_editor(initial_text, on_write, on_unload, keymaps) end -- Create buffer - local buf = buffer.create({ - name = "jujutsu:///DESCRIBE_EDITMSG", - split = "horizontal", - size = math.floor(vim.o.lines / 2), - filetype = "jjdescription", - buftype = "acwrite", - modifiable = true, - keymaps = keymaps, - }) + local buf = M.create_buffer() + -- Set keymaps before setting content to avoid triggering them during setup + buffer.set_keymaps(buf, keymaps or {}) -- Set buffer content vim.api.nvim_buf_set_lines(buf, 0, -1, false, initial_text) - -- Set bufhidden after creation - vim.bo[buf].bufhidden = "wipe" - -- Apply highlights initially apply_highlights(buf) -- Smart insert mode: insert when description is empty, normal mode otherwise - if M.auto_insert then + if M.opts.auto_insert then vim.schedule(function() if not vim.api.nvim_buf_is_valid(buf) then return @@ -184,4 +178,45 @@ function M.open_editor(initial_text, on_write, on_unload, keymaps) end end +--- Create the buffer based on the module options. +--- @return number buf Buffer number of the created buffer +function M.create_buffer() + if M.opts.window.type == "floating" then + local buf, _ = buffer.create_float({ + title = "JJ editor", + title_pos = "center", + filetype = "jjdescription", + buftype = "acwrite", + bufhidden = "wipe", + enter = true, + modifiable = true, + height = math.floor(vim.o.lines * M.opts.window.floating_height), + width = math.floor(vim.o.columns * M.opts.window.floating_width), + win_options = { + wrap = true, + number = false, + relativenumber = false, + cursorline = false, + signcolumn = "no", + winfixbuf = true, + }, + }) + + return buf + else + -- Get the lines/columns based on the direction of the split + local full_size = M.opts.window.type == "hsplit" and vim.o.lines or vim.o.columns + + return buffer.create({ + name = "jj:///DESCRIBE_EDITMSG", + filetype = "jjdescription", + buftype = "acwrite", + bufhidden = "wipe", + modifiable = true, + split = buffer.resolve_split(M.opts.window.type), + size = math.floor(full_size * M.opts.window.split_size), + }) + end +end + return M diff --git a/lua/jj/ui/terminal.lua b/lua/jj/ui/terminal.lua index 1489014..681e258 100644 --- a/lua/jj/ui/terminal.lua +++ b/lua/jj/ui/terminal.lua @@ -16,16 +16,7 @@ local utils = require("jj.utils") local buffer = require("jj.core.buffer") --- @type jj.ui.terminal.opts -local opts = { - cursor_render_delay = 10, - - window = { - type = "hsplit", - split_size = 0.5, - floating_width = 0.99, - floating_height = 0.95, - }, -} +local opts = {} --- @class jj.ui.terminal.state local state = { @@ -501,12 +492,9 @@ function M.run(cmd, keymaps) end -- Create new terminal buffer - local split_type = opts.window.type == "hsplit" and "horizontal" - or opts.window.type == "vsplit" and "vertical" - or opts.window.type == "tab" and "tab" local full_size = opts.window.type == "hsplit" and vim.o.lines or vim.o.columns state.buf = buffer.create({ - split = split_type, + split = buffer.resolve_split(opts.window.type), size = math.floor(full_size * opts.window.split_size), on_exit = function(buf) if state.buf == buf then