diff --git a/doc/channelot.txt b/doc/channelot.txt index dcb91c4..fb80832 100644 --- a/doc/channelot.txt +++ b/doc/channelot.txt @@ -80,6 +80,25 @@ a running job: end < +ChannelotCreateWindowForTerminalOpts *ChannelotCreateWindowForTerminalOpts* + + Fields: ~ + {bufnr?} (number) Use an existing buffer instead of creating a new one + + +M.create_window_for_terminal({opts?}) *channelot.create_window_for_terminal* + Create a new window suitable for running terminal jobs. + + * When the terminal window is closed, the focus will return (if possible) to + the original window from which this function was invoked. + * Automatically goes into insert mode inside the new window. + * Does not actually start the terminal. + + + Parameters: ~ + {opts?} (ChannelotCreateWindowForTerminalOpts) + + ChannelotTerminalOpts *ChannelotTerminalOpts* Fields: ~ @@ -92,6 +111,26 @@ M.terminal({opts?}) *channelot.terminal* Parameters: ~ {opts?} (ChannelotTerminalOpts) + Returns: ~ + (ChannelotTerminal) + + +M.windowed_terminal({opts?}) *channelot.windowed_terminal* + Similar to |channelot.terminal|, but automatically creates a window with + |channelot.create_window_for_terminal| to put the new terminal in. + + Parameters: ~ + {opts?} (ChannelotTerminalOpts) + + Returns: ~ + (ChannelotTerminal) + + +M.shadow_terminal() *channelot.shadow_terminal* + Similar to |channelot.terminal|, but without creating a window. + + A window can be created later using |ChannelotTerminal:expose|. + Returns: ~ (ChannelotTerminal) @@ -107,7 +146,22 @@ M.terminal_job({env}, {command}, {opts?}) Returns: ~ (ChannelotJob) @overload fun(command: string|string[]): ChannelotJob - @overload fun(command: string|string[], opts: table): ChannelotJob + @overload fun(command: string|string[], opts: ChannelotJobOptions): ChannelotJob + + + *channelot.windowed_terminal_job* +M.windowed_terminal_job({env}, {command}, {opts?}) + Similar to |channelot.terminal_job|, but automatically creates a window with + |channelot.create_window_for_terminal| to run the terminal job in. + + Parameters: ~ + {env} (table) Environment variables for the command + {command} (string|string[]) The command as a string or as a list of arguments + {opts?} (ChannelotJobOptions) + + Returns: ~ + (ChannelotJob) @overload fun(command: string|string[]): ChannelotJob + @overload fun(command: string|string[], opts: ChannelotJobOptions): ChannelotJob M.job({env}, {command}, {opts?}) *channelot.job* @@ -122,7 +176,7 @@ M.job({env}, {command}, {opts?}) *channelot.job* Returns: ~ (ChannelotJob) @overload fun(command: string|string[]): ChannelotJob - @overload fun(command: string|string[], opts: table): ChannelotJob + @overload fun(command: string|string[], opts: ChannelotJobOptions): ChannelotJob ChannelotTerminal *ChannelotTerminal* @@ -142,7 +196,7 @@ ChannelotTerminal:job({env}, {command}, {opts?}) Returns: ~ (ChannelotJob) @overload fun(command: string|string[]): ChannelotJob - @overload fun(command: string|string[], opts: table): ChannelotJob + @overload fun(command: string|string[], opts: ChannelotJobOptions): ChannelotJob ChannelotTerminal:raw_write({text}) *ChannelotTerminal:raw_write* @@ -187,6 +241,19 @@ ChannelotTerminal:get_bufnr() *ChannelotTerminal:get_bufnr* (number) The buffer number used by the terminal. +ChannelotTerminal:list_windows() *ChannelotTerminal:list_windows* + + Returns: ~ + (number[]) A list of window handles that contain the terminal + + +ChannelotTerminal:expose() *ChannelotTerminal:expose* + Create a window for the terminal using |channelot.create_window_for_terminal|. + + This is useful for a |channelot.shadow_terminal| that later needs to be + displayed - for example, if an error was encountered. + + ChannelotTerminal:close_buffer() *ChannelotTerminal:close_buffer* Close (delete) the buffer used by the terminal. diff --git a/lua/channelot/Terminal.lua b/lua/channelot/Terminal.lua index fb5861d..17608ae 100644 --- a/lua/channelot/Terminal.lua +++ b/lua/channelot/Terminal.lua @@ -13,7 +13,7 @@ local ChannelotTerminal = {} ---@param opts? ChannelotJobOptions ---@return ChannelotJob ---@overload fun(command: string|string[]): ChannelotJob ----@overload fun(command: string|string[], opts: table): ChannelotJob +---@overload fun(command: string|string[], opts: ChannelotJobOptions): ChannelotJob function ChannelotTerminal:job(env, command, opts) env, command, opts = require'channelot.util'.normalize_job_arguments(env, command, opts) local pty = require'channelot.util'.first_non_nil(opts.pty, true) @@ -112,6 +112,24 @@ function ChannelotTerminal:get_bufnr() return chan_info.buffer end +---@return number[] # A list of window handles that contain the terminal +function ChannelotTerminal:list_windows() + local bufnr = self:get_bufnr() + return vim.tbl_filter(function(win) + return vim.api.nvim_win_get_buf(win) == bufnr + end, vim.api.nvim_list_wins()) +end + +---Create a window for the terminal using |channelot.create_window_for_terminal|. +--- +---This is useful for a |channelot.shadow_terminal| that later needs to be +---displayed - for example, if an error was encountered. +function ChannelotTerminal:expose() + require'channelot'.create_window_for_terminal { + bufnr = self:get_bufnr(), + } +end + ---Close (delete) the buffer used by the terminal. function ChannelotTerminal:close_buffer() vim.api.nvim_buf_delete(self:get_bufnr(), {force = true}) diff --git a/lua/channelot/init.lua b/lua/channelot/init.lua index 7330a22..8c40cac 100644 --- a/lua/channelot/init.lua +++ b/lua/channelot/init.lua @@ -83,6 +83,43 @@ local M = {} ---< ---@brief ]] +---@class ChannelotCreateWindowForTerminalOpts +---@field bufnr? number Use an existing buffer instead of creating a new one + +---Create a new window suitable for running terminal jobs. +--- +---* When the terminal window is closed, the focus will return (if possible) to +--- the original window from which this function was invoked. +---* Automatically goes into insert mode inside the new window. +---* Does not actually start the terminal. +--- +---@param opts? ChannelotCreateWindowForTerminalOpts +function M.create_window_for_terminal(opts) + opts = opts or {} + local prev_win_id = vim.fn.win_getid(vim.fn.winnr()) + vim.cmd'botright 20new' + local bufnr + if opts.bufnr then + bufnr = opts.bufnr + vim.api.nvim_win_set_buf(0, bufnr) + else + bufnr = vim.api.nvim_get_current_buf() + end + vim.api.nvim_create_autocmd('WinEnter', { + buffer = bufnr, + callback = function() + prev_win_id = vim.fn.win_getid(vim.fn.winnr('#')) + end, + }) + vim.api.nvim_create_autocmd('WinClosed', { + buffer = bufnr, + callback = function() + vim.fn.win_gotoid(prev_win_id) + end, + }) + vim.cmd.startinsert() +end + ---@class ChannelotTerminalOpts ---@field bufnr? number Use the specified buffer instead of the current buffer @@ -107,13 +144,35 @@ function M.terminal(opts) return obj end +---Similar to |channelot.terminal|, but automatically creates a window with +---|channelot.create_window_for_terminal| to put the new terminal in. +---@param opts? ChannelotTerminalOpts +---@return ChannelotTerminal +function M.windowed_terminal(opts) + opts = opts or {} + M.create_window_for_terminal { + bufnr = opts.bufnr, + } + return M.terminal(opts) +end + +---Similar to |channelot.terminal|, but without creating a window. +--- +---A window can be created later using |ChannelotTerminal:expose|. +---@return ChannelotTerminal +function M.shadow_terminal() + return M.terminal { + bufnr = vim.api.nvim_create_buf(true, false), + } +end + ---Start a job on the current buffer, converting it to a terminal ---@param env table Environment variables for the command ---@param command string|string[] The command as a string or as a list of arguments ---@param opts? ChannelotJobOptions ---@return ChannelotJob ---@overload fun(command: string|string[]): ChannelotJob ----@overload fun(command: string|string[], opts: table): ChannelotJob +---@overload fun(command: string|string[], opts: ChannelotJobOptions): ChannelotJob function M.terminal_job(env, command, opts) env, command, opts = require'channelot.util'.normalize_job_arguments(env, command, opts) local pty = require'channelot.util'.first_non_nil(opts.pty, true) @@ -163,6 +222,19 @@ function M.terminal_job(env, command, opts) return obj end +---Similar to |channelot.terminal_job|, but automatically creates a window with +---|channelot.create_window_for_terminal| to run the terminal job in. +---@param env table Environment variables for the command +---@param command string|string[] The command as a string or as a list of arguments +---@param opts? ChannelotJobOptions +---@return ChannelotJob +---@overload fun(command: string|string[]): ChannelotJob +---@overload fun(command: string|string[], opts: ChannelotJobOptions): ChannelotJob +function M.windowed_terminal_job(env, command, opts) + M.create_window_for_terminal() + return M.terminal_job(env, command, opts) +end + ---Start a job without a terminal attached to it. --- ---Note: this job will not have a PTY, unless `{ pty = true }` is passed in the `opts`. @@ -171,7 +243,7 @@ end ---@param opts? ChannelotJobOptions ---@return ChannelotJob ---@overload fun(command: string|string[]): ChannelotJob ----@overload fun(command: string|string[], opts: table): ChannelotJob +---@overload fun(command: string|string[], opts: ChannelotJobOptions): ChannelotJob function M.job(env, command, opts) env, command, opts = require'channelot.util'.normalize_job_arguments(env, command, opts) local pty = require'channelot.util'.first_non_nil(opts.pty, false)