Skip to content

Commit

Permalink
Merge pull request #8 from Makaze/in-terminal
Browse files Browse the repository at this point in the history
Use terminal buffer for built in ANSI + shell support
  • Loading branch information
Makaze authored Apr 26, 2024
2 parents 01c05ec + fab282f commit e16658d
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 87 deletions.
78 changes: 9 additions & 69 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ started Neovim.
- [x] Option to watch for file changes
- [x] Backwards compability with `Neovim 0.9.5`
- [x] ANSI color support
- [x] Avoid dependency with `:terminal` buffer by default

# Requirements

* Neovim 0.9.5+
* [Makaze/AnsiEsc](https://github.com/Makaze/AnsiEsc) (optional, required for ANSI colors)
* [Makaze/AnsiEsc](https://github.com/Makaze/AnsiEsc) (optional, required for ANSI colors if `terminal` is `false`)

# Quickstart

Expand All @@ -55,72 +56,7 @@ Using [lazy.nvim](https://github.com/nvim-telescope/telescope.nvim):
```lua
{
"Makaze/watch.nvim",
dependencies = {
-- Optional. Add this if you want ANSI colors
{
"Makaze/AnsiEsc",
lazy = false,
config = function(_, opts)
vim.cmd([[
" Returns the #rrggbb hex value for a HL group.
"
" @function GetHLHex
" @description Returns the #rrggbb hex value for a HL group.
" @param group The highlight group to get. e.g. 'Comment'
" @param ground The value to get ('fg' or 'bg').
" @return The #rrggbb color value.
function! GetHLHex(group, ground)
" Get the syntax ID of the highlight group
let syn_id = synIDtrans(hlID(a:group))
" Get the RGB values of the foreground color in GUI mode
let hex_color = synIDattr(syn_id, a:ground . '#')
" Return the hex color
return hex_color
endfunction
let g:ansi_Black = '#1d2021'
let g:ansi_DarkRed = '#cc241d'
let g:ansi_DarkGreen = '#98971a'
let g:ansi_DarkYellow = '#d79921'
let g:ansi_DarkBlue = '#458588'
let g:ansi_DarkMagenta = '#b16286'
let g:ansi_DarkCyan = '#689d6a'
let g:ansi_LightGray = '#ebdbb2'
let g:ansi_DarkGray = '#a89984'
]])
end,
},
},
cmd = { "WatchStart", "WatchStop", "WatchFile" },
opts = {
-------------------- Default configuration -----------------------------
-- The default refresh rate for a new watcher in milliseconds. Defaults
-- to `500`.
refresh_rate = 500,
-- Whether to automatically delete the buffer when stopping a watcher.
-- Defaults to `false`.
close_on_stop = false,
-- Configuration for split window option
split = {
-- Whether to automatically delete the buffer when stopping a
-- watcher. Defaults to `false`.
enabled = false,
-- Where to place the split (above|below|right|left). Defaults to
-- `below`.
position = "below",
-- The size of the split in rows (or columns if position is right or
-- left). Defaults to `nil`.
size = nil,
-- Whether to focus on the newly created split watcher. Defaults to
-- `true`.
focus = true,
},
-- Whether to enable ANSI color output. Requires Makaze/AnsiEsc.
-- Defaults to `true`.
ANSI_enabled = true,
}
}
```

Expand Down Expand Up @@ -157,9 +93,13 @@ watch.setup({
-- `true`.
focus = true,
},
-- Whether to enable ANSI color output. Requires Makaze/AnsiEsc.
-- Defaults to `true`.
ANSI_enabled = true,
-- Whether to enable ANSI colors in output. Requires Makaze/AnsiEsc.
-- Ignored if `terminal` is set to `true`. Defaults to `false`.
ANSI_enabled = false,
-- Whether to open in a terminal buffer. Automatically supports your
-- terminal's built in ANSI colors. Has higher priority than
-- `ANSI_enabled`. Defaults to `true`.
terminal = true,
})
```

Expand Down
Binary file modified demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 40 additions & 5 deletions doc/watch.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Author: Makaze <[email protected]>
License: GPLv3.0
Version: 0.4.0
Version: 0.4.1

================================================================================
INTRODUCTION *watch*
Expand Down Expand Up @@ -39,10 +39,12 @@ Features: ~
[x] Option to watch for file changes
[x] Backwards compability with `Neovim 0.9.5`
[x] ANSI color support
[x] Avoid dependency with `:terminal` buffer by default

Requirements: ~
* Neovim 0.9.5+
* Makaze/AnsiEsc (optional, required for ANSI colors)
* Makaze/AnsiEsc (optional, required for ANSI colors if `terminal` is
`false`)

================================================================================
COMMANDS *watch-commands*
Expand Down Expand Up @@ -119,6 +121,13 @@ the standard configuration. You can change those options by calling
-- `true`.
focus = true,
},
-- Whether to enable ANSI colors in output. Requires Makaze/AnsiEsc.
-- Ignored if `terminal` is set to `true`. Defaults to `false`.
ANSI_enabled = false,
-- Whether to open in a terminal buffer. Automatically supports your
-- terminal's built in ANSI colors. Has higher priority than
-- `ANSI_enabled`. Defaults to `true`.
terminal = true,
})
>
Expand All @@ -133,10 +142,28 @@ watch.setup({opts*}) *watch.setup()*
{opts} (|watch.ConfigOverride|)
Keys (all keys optional): ~
`refresh_rate`: (integer) The default refresh rate for
a new watcher in milliseconds. Default 500.
a new watcher in milliseconds. Defaults to `500`.
`close_on_stop`: (boolean) Whether to automatically
delete the buffer when calling |watch.stop()|. Default
false.
delete the buffer when calling |watch.stop()|.
Defaults to `false`.
`split`: (table) Settings for auto-opening in a split.
`split.enabled`: (boolean) Whether to open in a split
by default. Defaults to `false`.
`split.position`: (string) Where to open the split
(above|below|left|right). Defaults to `"below"`.
`split.size`: (integer|nil) The size of the split in
rows (or columns if position is right or left).
Defaults to `nil`.
`split.focus`: (boolean) Whether to focus on the newly
created split watcher. Defaults to `true`.
`ANSI_enabled`: (boolean) Whether to enable ANSI colors
in output. Requires Makaze/AnsiEsc. Ignored if
`terminal` is set to `true`. Defaults to `false`.
`terminal`: (boolean) Whether to open in a terminal
buffer. Automatically supports your terminal's built
in ANSI colors. Has higher priority than
`ANSI_enabled`. Defaults to `true`.


watch.start({command}, {refresh_rate*}, {bufnr*}, {file*}) *watch.start()*
Starts continually reloading a buffer's contents with a shell command. If
Expand Down Expand Up @@ -201,6 +228,14 @@ watch.update_lines({lines}, {bufnr}) *watch.update_lines()*
{lines} (table) The lines to replace into the buffer.
{bufnr} (integer) The buffer number to udpate.

watch.update_term({command}, {bufnr}) *watch.update_term()*
Send {command} to the watch terminal buffer, preserving the scroll position.
Used internally by |watch.update()|.

Parameters: ~
{command} (string) Shell command to load to the buffer.
{bufnr} (integer) The buffer number to udpate.

================================================================================

vim: filetype=help expandtab tabstop=4 textwidth=80 colorcolumn=81
97 changes: 84 additions & 13 deletions lua/watch.lua
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ local function get_buf_by_name(name)
if vim.iter then
return vim.iter(A.nvim_list_bufs()):find(function(b)
local bufname = collapse_bufname(A.nvim_buf_get_name(b))
return bufname == name
return bufname == name or string.find(bufname, name, nil, true)
end)
else
for _, bufnr in ipairs(A.nvim_list_bufs()) do
local bufname = collapse_bufname(A.nvim_buf_get_name(bufnr))
if bufname == name then
if bufname == name or string.find(bufname, name, nil, true) then
return bufnr
end
end
Expand All @@ -67,6 +67,20 @@ local function get_buf_by_name(name)
return nil
end

--- Gets the command by the buffer name. Returns `nil` if not found.
---
--- @param bufname string The command name to get.
--- @return string|nil command
local function get_command_by_bufname(bufname)
for command, _ in pairs(Watch.watchers) do
if bufname == command or string.find(bufname, command, nil, true) then
return command
end
end

return nil
end

--- Get the time a file was last updated, or `nil` if <= the result of last check.
---
--- @param path string The absolute file path.
Expand Down Expand Up @@ -100,8 +114,8 @@ Watch.watchers = {}
---
--- @field refresh_rate integer The default refresh rate for a new watcher in milliseconds. Defaults to `500`.
--- @field close_on_stop boolean Whether to automatically delete the buffer when stopping a watcher. Defaults to `false`.
--- @field split watch.SplitConfig Configuration options for opening the watcher in a split.
--- @field ANSI_enabled boolean Whether to enable ANSI colors in output. Requires Makaze/AnsiEsc.vim. Defaults to `true`.
--- @field ANSI_enabled boolean Whether to enable ANSI colors in output. Requires Makaze/AnsiEsc. Ignored if `terminal` is set to `true`. Defaults to `false`.
--- @field terminal boolean Whether to open in a terminal buffer. Automatically supports your terminal's built in ANSI colors. Has higher priority than `ANSI_enabled`. Defaults to `true`.
---
--- Configuration for watch.nvim.

Expand All @@ -115,7 +129,8 @@ Watch.config = {
size = nil,
focus = true,
},
ANSI_enabled = true,
ANSI_enabled = false,
terminal = true,
}

--- @class watch.SplitConfigOverride
Expand All @@ -132,7 +147,9 @@ Watch.config = {
--- @field refresh_rate? integer The default refresh rate for a new watcher in milliseconds. Defaults to `500`.
--- @field close_on_stop? boolean Whether to automatically delete the buffer when stopping a watcher. Defaults to `false`.
--- @field split? watch.SplitConfigOverride Configuration options for opening the watcher in a split.
--- @field ANSI_enabled? boolean Whether to enable ANSI colors in output. Requires Makaze/AnsiEsc.vim. Defaults to `true`.
--- @field ANSI_enabled? boolean Whether to enable ANSI colors in output. Requires Makaze/AnsiEsc.vim. Ignored if terminal is set to `true`. Defaults to `false`.
--- @field terminal? boolean Whether to open in a terminal buffer. Automatically supports your terminal's built in ANSI colors. Has higher priority than ANSI_enabled. Defaults to `true`.
---
---
--- Configuration overrides for watch.nvim.

Expand All @@ -158,6 +175,51 @@ Watch.setup = function(opts)
vim.tbl_deep_extend("force", Watch.config, vim.F.if_nil(opts, {}))
end

--- Sends a command to a terminal buffer and executes it.
---
--- @param command string The command to send to the terminal.
--- @param bufnr integer The buffer number to update.
Watch.update_term = function(command, bufnr)
-- Save the current window ID and cursor position
local original_win = A.nvim_get_current_win()
local original_cursor = A.nvim_win_get_cursor(original_win)

-- Check if terminal buffer
local terminal_win = nil
-- Find the window ID associated with the specified buffer number
for _, win in ipairs(A.nvim_list_wins()) do
if A.nvim_win_get_buf(win) == bufnr then
terminal_win = win
break
end
end

-- Switch to the terminal window
if terminal_win then
A.nvim_win_call(terminal_win, function()
-- Send the command to the terminal buffer

vim.cmd("set modifiable")
A.nvim_buf_set_lines(bufnr, 0, -1, false, {})
vim.cmd("set nomodified")
vim.fn.termopen(command .. "\n")
vim.cmd("set modifiable")

-- Restore the original window and cursor position
if terminal_win == original_win then
A.nvim_win_set_cursor(original_win, original_cursor)
end
end)
else
vim.notify(
"[watch] ERROR: Terminal buffer with bufnr "
.. bufnr
.. " not found",
vim.log.levels.ERROR
)
end
end

--- Replaces the lines in a buffer while preserving the cursor.
---
--- @param lines table The lines to replace into the buffer.
Expand Down Expand Up @@ -217,8 +279,13 @@ Watch.update = function(command, bufnr)
end
end

-- Execute your command and capture its output
-- Use terminal if set
if Watch.config.terminal then
Watch.update_term(command, bufnr)
return
end

-- Execute your command and capture its output otherwise
if vim.system then
-- Use vim.system for async
local code = vim.system(
Expand Down Expand Up @@ -458,14 +525,18 @@ Watch.stop = function(event)
else
local command = event.file or event
local W = Watch.watchers[command]

if not W then
command = get_command_by_bufname(command) or ""
end
W = Watch.watchers[command]

-- Only error when not expected
if not W then
if not event or not event.event or event.event ~= "BufUnload" then
vim.notify(
"[watch] Error: Already not watching " .. command,
vim.log.levels.WARN
)
end
vim.notify(
"[watch] Error: Already not watching " .. command,
vim.log.levels.WARN
)

return
end
Expand Down

0 comments on commit e16658d

Please sign in to comment.