A Neovim plugin for mtlog-analyzer, providing real-time static analysis for Go code using the mtlog structured logging library.
- 🔍 Real-time Analysis - Automatic analysis on save with debouncing
- 🎯 Smart Activation - Automatically enables for Go projects
- 💡 Quick Fixes - Apply suggested fixes with code actions
- 📊 Statusline Integration - Display diagnostic counts in your statusline
- ⚡ Performance Optimized - Caching, rate limiting, and async operations
- 🛠️ Highly Configurable - Customize every aspect of the plugin
- Neovim >= 0.8.0
- Go >= 1.21
- mtlog-analyzer installed
Using lazy.nvim
{
'willibrandon/mtlog',
lazy = false, -- Load immediately to ensure commands are available
config = function(plugin)
-- Handle the plugin's subdirectory structure
vim.opt.rtp:append(plugin.dir .. "/neovim-plugin")
vim.cmd("runtime plugin/mtlog.vim")
require('mtlog').setup({
-- your configuration
})
end,
ft = 'go', -- Only load for Go files
}Note for lazy.nvim users: This plugin is part of a monorepo with its Neovim files in the
neovim-pluginsubdirectory. The lazy.nvim configuration above handles this structure by manually adjusting the runtime path.
Using packer.nvim
use {
'willibrandon/mtlog',
rtp = 'neovim-plugin',
ft = { 'go' },
config = function()
require('mtlog').setup({
-- your configuration
})
end,
}Using vim-plug
Plug 'willibrandon/mtlog', { 'rtp': 'neovim-plugin', 'for': 'go' }
" In your init.vim or init.lua
lua require('mtlog').setup()go install github.com/willibrandon/mtlog/cmd/mtlog-analyzer@latestrequire('mtlog').setup({
-- Path to mtlog-analyzer executable
analyzer_path = 'mtlog-analyzer',
-- Automatically enable for Go projects
auto_enable = true,
-- Automatically analyze on save/change
auto_analyze = true,
-- Debounce time in milliseconds
debounce_ms = 500,
-- Virtual text configuration
virtual_text = {
enabled = true,
prefix = '■ ',
spacing = 2,
severity_limit = vim.diagnostic.severity.HINT,
},
-- Sign column configuration
signs = {
enabled = true,
priority = 10,
text = {
[vim.diagnostic.severity.ERROR] = '✗',
[vim.diagnostic.severity.WARN] = '⚠',
[vim.diagnostic.severity.INFO] = 'ℹ',
[vim.diagnostic.severity.HINT] = '💡',
},
},
-- Underline configuration
underline = {
enabled = true,
severity_limit = vim.diagnostic.severity.WARN,
},
-- Severity mappings for diagnostic codes
severity_levels = {
MTLOG001 = vim.diagnostic.severity.ERROR, -- Template/argument mismatch
MTLOG002 = vim.diagnostic.severity.ERROR, -- Invalid format specifier
MTLOG003 = vim.diagnostic.severity.ERROR, -- Missing error in Error/Fatal
MTLOG004 = vim.diagnostic.severity.WARN, -- Non-PascalCase property
MTLOG005 = vim.diagnostic.severity.WARN, -- Complex type needs LogValue
MTLOG006 = vim.diagnostic.severity.WARN, -- Duplicate property
MTLOG007 = vim.diagnostic.severity.HINT, -- String context key suggestion
},
-- Rate limiting
rate_limit = {
enabled = true,
max_files_per_second = 10,
},
-- Cache configuration
cache = {
enabled = true,
ttl_seconds = 300, -- 5 minutes
},
-- Show error notifications
show_errors = true,
-- Custom analyzer flags
analyzer_flags = {},
-- File patterns to ignore
ignore_patterns = {
'vendor/',
'%.pb%.go$',
'_test%.go$',
},
})require('mtlog').setup({
-- Disable virtual text, keep only signs
virtual_text = false,
-- Simpler signs
signs = {
text = {
[vim.diagnostic.severity.ERROR] = 'E',
[vim.diagnostic.severity.WARN] = 'W',
[vim.diagnostic.severity.INFO] = 'I',
[vim.diagnostic.severity.HINT] = 'H',
},
},
})| Command | Description |
|---|---|
:MtlogAnalyze [file] |
Analyze current buffer or specified file |
:MtlogAnalyzeWorkspace |
Analyze entire workspace |
:MtlogClear[!] |
Clear diagnostics (! for all buffers) |
:MtlogEnable |
Enable analyzer |
:MtlogDisable |
Disable analyzer |
:MtlogToggle |
Toggle analyzer |
:MtlogStatus |
Show plugin status |
:MtlogCache {clear|stats} |
Manage cache |
:MtlogQueue {show|stats|clear|pause|resume} |
Manage analysis queue |
:MtlogContext {show|test|add-builtin|clear} |
Manage context rules |
:MtlogQuickFix |
Apply quick fix at cursor |
:MtlogToggleDiagnostics |
Toggle global diagnostics kill switch |
:MtlogSuppress [id] |
Suppress a diagnostic ID |
:MtlogUnsuppress [id] |
Unsuppress a diagnostic ID |
:MtlogShowSuppressions |
Show suppressed diagnostics |
:MtlogCodeAction |
Show mtlog code actions menu |
The plugin integrates with Neovim's built-in LSP client to provide code actions through the standard interface.
- Native Code Actions - mtlog quick fixes appear in
vim.lsp.buf.code_action()menu - Suppress Actions - Suppress diagnostics directly from the code actions menu
- Standard Keybindings - Works with your existing LSP keybindings (e.g.,
<leader>ca)
require('mtlog').setup({
lsp_integration = {
enabled = true, -- Enable LSP integration (default: true)
show_suppress_action = true, -- Show suppress action in menu (default: true)
},
})With LSP integration enabled, you can:
-
Use standard LSP code action keybinding:
vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, { desc = 'Code action' })
-
Filter for mtlog actions only:
vim.keymap.set('n', '<leader>cm', function() vim.lsp.buf.code_action({ filter = function(action) return action.data and action.data.source == 'mtlog-analyzer' end, }) end, { desc = 'mtlog code actions' })
-
Use the dedicated command:
:MtlogCodeAction
The LSP integration works seamlessly with:
- telescope.nvim -
<leader>cawith Telescope UI - fzf-lua - Code actions with fuzzy finding
- nvim-cmp - Code action completion
- null-ls - Alongside other code action providers
Example keybindings you can add to your configuration:
vim.keymap.set('n', '<leader>ma', ':MtlogAnalyze<CR>', { desc = 'Analyze current file' })
vim.keymap.set('n', '<leader>mw', ':MtlogAnalyzeWorkspace<CR>', { desc = 'Analyze workspace' })
vim.keymap.set('n', '<leader>mf', ':MtlogQuickFix<CR>', { desc = 'Apply quick fix' })
vim.keymap.set('n', '<leader>mc', ':MtlogClear<CR>', { desc = 'Clear diagnostics' })
vim.keymap.set('n', '<leader>mh', ':MtlogHelp<CR>', { desc = 'Show help' })
vim.keymap.set('n', '<leader>m?', ':MtlogExplain<CR>', { desc = 'Explain diagnostic' })
vim.keymap.set('n', ']m', function() require('mtlog.diagnostics').goto_next() end, { desc = 'Next mtlog diagnostic' })
vim.keymap.set('n', '[m', function() require('mtlog.diagnostics').goto_prev() end, { desc = 'Previous mtlog diagnostic' })The plugin includes a comprehensive interactive help system:
:MtlogHelp- Interactive help menu with topics:MtlogHelp quick- Quick reference card:MtlogExplain- Explain diagnostic at cursor:MtlogExplain MTLOG001- Explain specific diagnostic code:help mtlog- Full Vim documentation
First-time users will see a welcome message with quick start instructions.
-- Add to your statusline
vim.o.statusline = vim.o.statusline .. ' %{luaeval("require(\'mtlog.statusline\').get_component()")}'require('lualine').setup({
sections = {
lualine_c = {
require('mtlog.statusline').lualine_component(),
},
},
})-- Get diagnostic counts
local counts = require('mtlog').get_counts()
print(string.format('Errors: %d, Warnings: %d', counts.errors, counts.warnings))
-- Get formatted component
local component = require('mtlog.statusline').get_component({
nerd_fonts = true,
format = 'short', -- 'minimal', 'short', or 'long'
show_zero = false,
})local mtlog = require('mtlog')
-- Setup with configuration
mtlog.setup(opts)
-- Enable/disable analyzer
mtlog.enable()
mtlog.disable()
mtlog.toggle()
-- Analyze files
mtlog.analyze_buffer(bufnr)
mtlog.analyze_workspace()
-- Clear diagnostics
mtlog.clear(bufnr)
mtlog.clear_all()
-- Get diagnostic counts
local counts = mtlog.get_counts()
-- Check analyzer availability
local available = mtlog.is_available()
local version = mtlog.get_version()local diagnostics = require('mtlog.diagnostics')
-- Navigate diagnostics
diagnostics.goto_next()
diagnostics.goto_prev()
-- Get diagnostic at cursor
local diag = diagnostics.get_diagnostic_at_cursor()
-- Apply suggested fix
diagnostics.apply_suggested_fix(diag, fix_index)
-- Show diagnostic float
diagnostics.show_float()
-- Set to quickfix/location list
diagnostics.setqflist()
diagnostics.setloclist()Note: This feature is exclusive to the Neovim plugin, providing advanced control not available in VS Code or GoLand extensions.
Context rules allow you to automatically enable, disable, or ignore analysis based on file paths, buffer properties, or custom conditions.
The plugin includes several built-in rules that can be enabled:
require('mtlog').setup({
use_builtin_rules = true, -- Enable built-in rules
})Built-in rules include:
- ignore_vendor - Ignore files in vendor directories
- ignore_testdata - Ignore test data files
- disable_large_files - Disable for files > 100KB
Define custom rules to control when analysis runs:
require('mtlog').setup({
context_rules = {
-- Path-based rule (glob pattern)
{
type = 'path',
pattern = '*/generated/*',
action = 'ignore',
description = 'Ignore generated files',
},
-- Path-based rule (regex)
{
type = 'path',
pattern = '.*\\.pb\\.go$',
regex = true,
action = 'ignore',
description = 'Ignore protobuf files',
},
-- Buffer property rule
{
type = 'buffer',
line_count = { max = 10000 }, -- Trigger for files > 10000 lines
action = 'disable',
description = 'Disable for very large files',
},
-- Project marker rule
{
type = 'project',
markers = { 'go.mod' },
marker_content = {
['go.mod'] = 'github.com/willibrandon/mtlog',
},
action = 'enable',
analyze_immediately = true,
description = 'Auto-enable for mtlog projects',
},
-- Custom matcher function
{
type = 'custom',
action = 'disable',
matcher = function(bufnr, filepath)
-- Custom logic here
return filepath:match('_test%.go$') ~= nil
end,
description = 'Disable for test files',
},
},
})pattern- Glob or regex pattern to match file pathsregex- Set to true to use regex instead of glob
modified- Match modified/unmodified buffersreadonly- Match readonly buffersbuftype- Match specific buffer types ('nofile', 'terminal', etc.)filesize- Match based on file size:max- Trigger when size exceeds this valuemin- Trigger when size is below this value
line_count- Match based on line count:max- Trigger when lines exceed this valuemin- Trigger when lines are below this value
markers- Files/directories that identify project root (e.g., 'go.mod', '.git')marker_content- Optional content patterns to match in marker files
matcher- Function that receives (bufnr, filepath) and returns boolean
enable- Enable the analyzerdisable- Disable the analyzer (can be re-enabled manually)ignore- Permanently ignore this buffercustom- Run a custom handler function
" Show current context rules
:MtlogContext show
" Test rules against current buffer
:MtlogContext test
" Add a built-in rule interactively
:MtlogContext add-builtin
" Clear all context rules
:MtlogContext clear{
type = 'path',
pattern = '*.generated.go',
action = 'ignore',
}{
type = 'buffer',
filesize = { max = 500000 }, -- 500KB
action = 'disable',
}{
type = 'project',
markers = { '.mtlog-enabled' },
action = 'enable',
}{
type = 'custom',
matcher = function(bufnr, filepath)
-- Skip integration tests but analyze unit tests
return filepath:match('integration_test%.go$') ~= nil
end,
action = 'ignore',
}Run :checkhealth mtlog to verify your installation:
- Neovim version compatibility
- Go installation
- mtlog-analyzer availability
- Current configuration
- Plugin status
If commands like :MtlogStatus are not available after installation with lazy.nvim:
- Ensure you're using the configuration shown above with
lazy = false - The config function must include both the runtime path adjustment and the explicit runtime command
- Check that the plugin files exist:
ls ~/.local/share/nvim/lazy/mtlog/neovim-plugin/plugin/
Install mtlog-analyzer:
go install github.com/willibrandon/mtlog/cmd/mtlog-analyzer@latestMake sure $GOPATH/bin is in your PATH.
- Check if the plugin is enabled:
:MtlogStatus - Verify analyzer is working:
mtlog-analyzer your-file.go - Check for errors:
:messages - Clear cache:
:MtlogCache clear
Adjust debouncing and rate limiting:
require('mtlog').setup({
debounce_ms = 1000, -- Increase debounce time
rate_limit = {
max_files_per_second = 5, -- Reduce rate limit
},
})The plugin integrates with Neovim's built-in LSP client to provide code actions. When you have an LSP client attached, mtlog quick fixes will appear in the code actions menu (usually triggered with <leader>ca or :lua vim.lsp.buf.code_action()).
Contributions are welcome! Please feel free to submit issues and pull requests.
This plugin is part of the mtlog project and follows the same license.
