Skip to content

Conversation

@S1M0N38
Copy link

@S1M0N38 S1M0N38 commented Jan 5, 2026

Summary

Add support for highlighting log level keywords (TRACE, DEBUG, INFO, WARN, WARNING, ERROR) using Neovim's diagnostic highlight groups.

Features

  • Highlights uppercase log level keywords with word-boundary matching
  • Uses diagnostic highlight groups by default (DiagnosticError, DiagnosticWarn, DiagnosticInfo, DiagnosticHint)
  • Customizable highlight groups via log_level_groups config
  • Disabled by default (opt-in via log_level_hl = true in setup)
  • Works for both regular buffers and file-based tailing

New Commands

  • :TailLogLevelHlEnable
  • :TailLogLevelHlDisable
  • :TailLogLevelHlToggle

New Lua API

require("tail").log_level_hl_enable(bufnr, { backfill = true })
require("tail").log_level_hl_disable(bufnr)
require("tail").log_level_hl_toggle(bufnr, { backfill = false })

Configuration

require("tail").setup({
  log_level_hl = true,  -- enable by default
  log_level_groups = {  -- customize highlight groups
    TRACE = "DiagnosticHint",
    DEBUG = "DiagnosticHint",
    INFO = "DiagnosticInfo",
    WARN = "DiagnosticWarn",
    WARNING = "DiagnosticWarn",
    ERROR = "DiagnosticError",
  },
})

- Add log_level_hl and log_level_groups to default config
- Add highlight_log_levels() function with word-boundary matching
- Add backfill_log_levels() for existing buffer content
- Track log_level_hl state per buffer
- Hook into on_lines callback to highlight new lines
- Add public API: log_level_hl_enable/disable/toggle

Supports uppercase keywords: TRACE, DEBUG, INFO, WARN, WARNING, ERROR
Uses Neovim's diagnostic highlight groups by default.
- Add highlight_log_levels() function for file-tail buffers
- Add file_log_level_hl_enable/disable functions
- Hook highlighting into file poller callback
- Add commands: TailLogLevelHlEnable/Disable/Toggle
- Update description and features list
- Add log_level_hl config option to setup example
- Document new commands and Lua API functions
Copilot AI review requested due to automatic review settings January 5, 2026 16:11
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for highlighting log level keywords (TRACE, DEBUG, INFO, WARN, WARNING, ERROR) in tail.nvim buffers. The feature is opt-in and uses Neovim's diagnostic highlight groups by default, with support for customization.

Key Changes:

  • Implements log level highlighting using frontier patterns for word boundary matching
  • Adds new configuration options (log_level_hl and log_level_groups)
  • Provides new commands and Lua API functions for controlling the feature at runtime

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
plugin/tail.lua Adds log level highlighting implementation for file-based tail buffers, including namespace creation, highlighting logic, enable/disable functions, and new user commands
lua/tail/init.lua Implements log level highlighting for non-file buffers with configuration support, integrates with the on_lines handler, and exposes public API functions
README.md Documents the new log level highlighting feature, including usage examples and API reference

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

cfg = {
follow = true,
ts_enabled = false,
ts_format = "%Y-%m-%d %H:%M:%S ",
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default config created here is missing the ts_hl field which is present in other similar initializations (see line 204 and file_timestamps_enable around line 356). This inconsistency could lead to bugs if code expects ts_hl to be present. The field should be initialized to maintain consistency with other config initializations.

Suggested change
ts_format = "%Y-%m-%d %H:%M:%S ",
ts_format = "%Y-%m-%d %H:%M:%S ",
ts_hl = false,

Copilot uses AI. Check for mistakes.
Comment on lines 48 to 57
require("tail").setup({
-- uncomment the next line to enable timestamps by default
-- timestamps = true,
-- enable timestamps by default
timestamps = false,
-- customise the format (see `:help os.date`)
timestamp_format = "%Y-%m-%d %H:%M:%S",
-- customise the highlight group used for the timestamp
timestamp_hl = "Comment",
-- enable log level highlighting by default
log_level_hl = false,
})
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README documentation is incomplete. According to the PR description, users should be able to customize highlight groups via the log_level_groups config option, but this is not documented in the setup example. Consider adding documentation showing users how to customize the highlight groups, similar to how the PR description shows this feature.

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +38
local log_level_groups = {
TRACE = "DiagnosticHint",
DEBUG = "DiagnosticHint",
INFO = "DiagnosticInfo",
WARN = "DiagnosticWarn",
WARNING = "DiagnosticWarn",
ERROR = "DiagnosticError",
}
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The highlight groups are hardcoded here in plugin/tail.lua but should respect the user's configuration from tail.opts.log_level_groups. Currently, if a user configures custom highlight groups via setup(), they will work for non-file buffers but not for file-based tail buffers because this local table is not reading from the config. Consider accessing tail.opts.log_level_groups instead.

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +73
local ns_loglevel = vim.api.nvim_create_namespace("tail-loglevel")

-- default log level highlight groups (same as init.lua)
local log_level_groups = {
TRACE = "DiagnosticHint",
DEBUG = "DiagnosticHint",
INFO = "DiagnosticInfo",
WARN = "DiagnosticWarn",
WARNING = "DiagnosticWarn",
ERROR = "DiagnosticError",
}

--- Highlight log level keywords in the given line range (1-based, inclusive)
---@param bufnr number
---@param start_line number 1-based start line
---@param end_line number 1-based end line (inclusive)
local function highlight_log_levels(bufnr, start_line, end_line)
if not vim.api.nvim_buf_is_valid(bufnr) then
return
end

local lines = vim.api.nvim_buf_get_lines(bufnr, start_line - 1, end_line, false)

for i, line in ipairs(lines) do
local lnum = start_line + i - 1

for keyword, hl_group in pairs(log_level_groups) do
-- Use frontier pattern for word boundary matching (uppercase only)
local pattern = "%f[%w]" .. keyword .. "%f[%W]"
local search_start = 1

while true do
local match_start, match_end = line:find(pattern, search_start)
if not match_start then
break
end

vim.api.nvim_buf_set_extmark(bufnr, ns_loglevel, lnum - 1, match_start - 1, {
end_col = match_end,
hl_group = hl_group,
})

search_start = match_end + 1
end
end
end
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is duplicated in lua/tail/init.lua (lines 157-187). The two implementations are nearly identical, which creates a maintainability issue. Consider extracting this to a shared utility module to avoid code duplication and ensure consistent behavior across file and non-file buffers.

Suggested change
local ns_loglevel = vim.api.nvim_create_namespace("tail-loglevel")
-- default log level highlight groups (same as init.lua)
local log_level_groups = {
TRACE = "DiagnosticHint",
DEBUG = "DiagnosticHint",
INFO = "DiagnosticInfo",
WARN = "DiagnosticWarn",
WARNING = "DiagnosticWarn",
ERROR = "DiagnosticError",
}
--- Highlight log level keywords in the given line range (1-based, inclusive)
---@param bufnr number
---@param start_line number 1-based start line
---@param end_line number 1-based end line (inclusive)
local function highlight_log_levels(bufnr, start_line, end_line)
if not vim.api.nvim_buf_is_valid(bufnr) then
return
end
local lines = vim.api.nvim_buf_get_lines(bufnr, start_line - 1, end_line, false)
for i, line in ipairs(lines) do
local lnum = start_line + i - 1
for keyword, hl_group in pairs(log_level_groups) do
-- Use frontier pattern for word boundary matching (uppercase only)
local pattern = "%f[%w]" .. keyword .. "%f[%W]"
local search_start = 1
while true do
local match_start, match_end = line:find(pattern, search_start)
if not match_start then
break
end
vim.api.nvim_buf_set_extmark(bufnr, ns_loglevel, lnum - 1, match_start - 1, {
end_col = match_end,
hl_group = hl_group,
})
search_start = match_end + 1
end
end
end
-- re-use the namespace from the core tail module if available, so highlights stay consistent
local ns_loglevel = tail.ns_loglevel or vim.api.nvim_create_namespace("tail-loglevel")
--- Highlight log level keywords in the given line range (1-based, inclusive)
--- Delegates to the implementation in the core `tail` module to avoid code duplication.
---@param bufnr number
---@param start_line number 1-based start line
---@param end_line number 1-based end line (inclusive)
local function highlight_log_levels(bufnr, start_line, end_line)
-- If the core module exposes a shared implementation, delegate to it.
if type(tail.highlight_log_levels) == "function" then
return tail.highlight_log_levels(bufnr, start_line, end_line)
end
-- Fallback: keep behavior safe (no-op) if the shared implementation is not available.
if not vim.api.nvim_buf_is_valid(bufnr) then
return
end

Copilot uses AI. Check for mistakes.
@S1M0N38
Copy link
Author

S1M0N38 commented Jan 5, 2026

The code and this PR were AI-generated. I’m testing this new feature locally, and it works nicely so far. This PR can be used:

  • as a reference for a manual implementation
  • to showcase the idea of diagnostic highlight groups to highlight common log levels

Alternatively, it can be closed without additional comments if it falls outside the scope of this plugin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant