From 1ffe5d59b01bacedc64bc05d7732f3036d52cce5 Mon Sep 17 00:00:00 2001 From: Blackmorse Date: Wed, 22 Jul 2026 00:54:42 +0400 Subject: [PATCH] feat: add floating terminal window option for flix commands --- README.md | 16 +++++++++++ lua/flix/commands.lua | 63 ++++++++++++++++++++++++++++++++++++++++--- lua/flix/config.lua | 26 ++++++++++++++++++ 3 files changed, 102 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 33e7c66..e2df2bc 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,14 @@ require("flix").setup({ java = "java", -- executable used to launch the jar jar = "flix.jar", -- relative paths resolve from the project root root_markers = { "flix.toml" }, + terminal = { -- how `flix_cmd` opens its terminal + window = "split", -- "split" (default) or "float" (floating window) + width = 0.60, -- float = fraction of editor, integer = columns + height = 0.5, -- float = fraction of editor, integer = rows + pos_x = "center", -- number or "center" (float = fraction of width) + pos_y = "center", -- number or "center" (float = fraction of height) + close_binding = "q", -- Normal-mode key that closes the floating window + }, features = { codelens = true, -- refresh code lenses while editing completion = false, -- native autocompletion (see below) @@ -78,6 +86,14 @@ require("flix").setup({ }) ``` +### Floating terminal + +By default `flix_cmd` opens in a `belowright split`. Set `terminal.window = "float"` +to open it in a floating window instead. The remaining `terminal` fields only +apply to the float: `width`/`height` accept a fraction of the editor size (e.g. +`0.5`) or an integer column/row count, `pos_x`/`pos_y` accept a number or +`"center"`, and `close_binding` (default `q`) closes the window from Normal mode. + ### Native completion (Neovim 0.11+) Set `features.completion = true` to get insert-mode autocompletion straight from diff --git a/lua/flix/commands.lua b/lua/flix/commands.lua index fc7e773..19e7c58 100644 --- a/lua/flix/commands.lua +++ b/lua/flix/commands.lua @@ -2,6 +2,36 @@ local config = require("flix.config") local M = {} +--- resolve a size spec to an integer number of cells +--- a whole number is used as-is (columns/rows); a fractional value is a fraction of `total` +--- @param spec number +--- @param total integer +--- @return integer +local function resolve_size(spec, total) + if spec % 1 ~= 0 then + return math.max(1, math.floor(spec * total)) + end + return spec +end + +--- resolve a position spec to an integer cell offset +--- "center" centers a window of `size` cells within `total`; +--- a whole number is used as-is; a fractional value is a fraction of `total` +--- @param spec number|string +--- @param total integer +--- @param size integer +--- @return integer +local function resolve_pos(spec, total, size) + if spec == "center" then + return math.floor((total - size) / 2) + end + if type(spec) == "number" and spec % 1 ~= 0 then + return math.floor(spec * total) + end +---@diagnostic disable-next-line: return-type-mismatch + return spec +end + --- run `java -jar ` from the Flix project root in a terminal split --- works without the LSP being attached: the root is found from `root_markers` ---@param cmd string the Flix CLI subcommand, e.g. "run" or "test" @@ -28,9 +58,36 @@ function M.flix_cmd(cmd) return end - vim.cmd("belowright split") - local term_buf = vim.api.nvim_create_buf(false, true) - vim.api.nvim_win_set_buf(0, term_buf) + local t = o.terminal + + if t.window == "float" then + local term_buf = vim.api.nvim_create_buf(false, true) + + local total_cols = vim.o.columns + local total_rows = vim.o.lines + + local width = resolve_size(t.width, total_cols) + local height = resolve_size(t.height, total_rows) + local col = resolve_pos(t.pos_x, total_cols, width) + local row = resolve_pos(t.pos_y, total_rows, height) + + vim.api.nvim_open_win(term_buf, true, { + relative = "win", + width = width, + height = height, + row = row, + col = col, + title = "Flix Run", + title_pos = "center", + border = "single" + }) + + vim.keymap.set("n", t.close_binding, "bdelete!", { buffer = term_buf, silent = true }) + else + vim.cmd("belowright split") + local term_buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_win_set_buf(0, term_buf) + end vim.fn.jobstart({ o.java, "-jar", jar, cmd }, { term = true, diff --git a/lua/flix/config.lua b/lua/flix/config.lua index b8c806e..d164db2 100644 --- a/lua/flix/config.lua +++ b/lua/flix/config.lua @@ -1,11 +1,36 @@ local M = {} +---@class flix.TerminalOptions +---@field window "float"|"split" how the terminal is opened +---@field width number integer = columns; float in (0,1) = fraction of editor width +---@field height number integer = rows; float in (0,1) = fraction of editor height +---@field pos_x number|"center" integer = columns from left; float = fraction of editor width; "center" = centered on x +---@field pos_y number|"center" integer = rows from top; float = fraction of editor height; "center" = centered on y +---@field close_binding string Normal-mode key that closes the terminal window + +---@class flix.Config +---@field java string path or name of the java executable +---@field jar string path to flix.jar, relative to the project root or absolute +---@field root_markers string[] files used to detect the project root +---@field terminal flix.TerminalOptions options for the floating terminal window +---@field features table feature flags +---@field lsp table options forwarded to `vim.lsp.config("flix", ...)` + -- default configuration -- override any field via `require("flix").setup({ ... })` +---@type flix.Config M.defaults = { java = "java", jar = "flix.jar", root_markers = { "flix.toml" }, + terminal = { + window = "split", + width = 0.60, + height = 0.5, + pos_x = "center", + pos_y = "center", + close_binding = "q" + }, features = { codelens = true, completion = false, @@ -14,6 +39,7 @@ M.defaults = { } -- active configuration +---@type flix.Config M.options = vim.deepcopy(M.defaults) --- merge user options over the defaults