Skip to content

chrisgrieser/nvim-chainsaw

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

nvim-chainsaw πŸͺš

badge

Quick and feature-rich insertion of various kinds of log statements.

demo.mp4

Features

  • Quick insertion of log statements for the variable under the cursor (normal mode) or the selection (visual mode).
  • Support for a dozen different log statement types, such as assert statements, stacktraces, audible "logs", and more.
  • Builtin support for ~20 common languages, with dedicated support for nvim-lua, and easy configuration for additional languages.
  • Helper commands to remove all log statements created by nvim-chainsaw or clear the console.
  • Smart detection of the variable under the cursor via Treesitter.
  • Flexible templating options for customizing the log statements inserted, including support for multi-line templates.
  • All commands are dot-repeatable.

Installation

Requirements

-- lazy.nvim
{ "chrisgrieser/nvim-chainsaw" },

-- packer
use { "chrisgrieser/nvim-chainsaw" }

Built-in language support

  • JavaScript/TypeScript (and supersets)
  • Python
  • Lua (with special considerations for nvim-lua1)
  • Shell (and supersets)
  • AppleScript
  • Ruby
  • Rust
  • CSS2 (and supersets)
  • Go3

Not every language supports every type of log statement. For the concrete statements used, see log-statements-data.lua.

Usage

The plugin offers various types of log statements. Bind keymaps for the ones you want to use.

All operations are dot-repeatable.

List of commands

-- log the name & value of the variable under the cursor
require("chainsaw").variableLog()

-- like variableLog, but with syntax specific to inspect an object such as
-- `console.log(JSON.stringify(foobar))` in javascript
require("chainsaw").objectLog()

-- inspect the type of the variable under cursor, such as `typeof foo` in js
require("chainsaw").typeLog()

-- assertion statement for variable under cursor
require("chainsaw").assertLog()

-- Minimal log statement, with an emoji for differentiation. Intended for
-- control flow inspection, that is to quickly glance whether a condition was
-- triggered or not.
require("chainsaw").emojiLog()

-- Sound-playing statement for audible debugging.
-- Depending on the type of log statement, it is either a terminal bell
-- (requiring the terminal) or a system sound.
-- Inspired by https://news.ycombinator.com/item?id=41519046
require("chainsaw").sound()

-- create log statement, and position the cursor to enter a message
require("chainsaw").messageLog()

-- 1st call: start measuring the time
-- 2nd call: logs the time duration since
require("chainsaw").timeLog()

-- debug statements like `debugger` in javascript or `breakpoint()` in python
require("chainsaw").debugLog()

-- prints the stacktrace of the current call
require("chainsaw").stacktraceLog()

-- clearing statement, such as `console.clear()`
require("chainsaw").clearLog()

---------------------------------------------------

-- remove all log statements created by nvim-chainsaw
require("chainsaw").removeLogs()

These features can also be accessed with the user command :Chainsaw. Each option corresponds to the commands above. For example, :Chainsaw variableLog is same as :lua require("chainsaw").variableLog().

When using lua functions, variableLog, objectLog, typeLog, and assertLog can also be used in visual mode to use the visual selection instead of the word under the cursor.

Smart variable identification

When the variable under the cursor is an object with fields, chainsaw attempts to automatically select the correct field. Note that this feature requires the Treesitter parser of the respective language.

myVariable.myF[i]eld = "foobar"
-- prints: myVariable.myField

myVa[r]iable.myField = "foobar"
-- prints: myVariable

Filetypes currently supporting this feature:

  • Lua
  • Python
  • JavaScript (and supersets)

PRs adding support for more languages are welcome.

Configuration

Base configuration

-- default settings
require("chainsaw").setup {
	-- The marker should be a unique string, since lines with it are highlighted
	-- and since `.removeLogs()` will remove any line with it. Thus, emojis or
	-- unique strings like "[Chainsaw]" are recommended.
	marker = "πŸͺš",

	-- Highlight lines with the marker.
	-- When using `lazy.nvim`, you need to add `event = VeryLazy` to the plugin
	-- spec to have existing log statements highlighted as well.
	---@type string|false
	logHighlightGroup = "Visual",

	-- emojis used for `emojiLog`
	logEmojis = { "πŸ”΅", "🟩", "⭐", "β­•", "πŸ’œ", "πŸ”²" },
}

Custom log statements

Custom log statements can be added in the setup() call. There are various placeholders that are dynamically replaced:

  • {{marker}} inserts the value from config.marker. Each log statement should have one, so that the line can be removed via .removeLogs().
  • {{var}}: variable as described further above.
  • {{time}}: timestamp formatted as HH:MM:SS (for millisecond-precision, use .timeLog() instead)
  • {{file}}: basename of the current file
  • {{lnum}}: current line number
  • .emojiLog() only: {{emoji}} inserts the emoji
  • .timeLog() only: {{index}} inserts a running index (needed to differentiate between variables when inserting timeLog multiple times).

See log-statements-data.lua for the built-in log statements. PRs adding log statements for more languages are welcome.

require("chainsaw").setup ({
	logStatements = {
		variableLog = {
			javascript = 'console.log("{{marker}} {{var}}:", {{var}});',
			otherFiletype = … -- <-- add the statement for your filetype here
		},
		-- the same way for the other log statement operations
	},
})

Note

  1. The strings may not include line breaks. If you want to use multi-line log statements, use a list of strings instead, each string representing one line.
  2. See superset-inheritance.lua for how language supersets (such as typescript inheriting from javascript) is handled.

Have your formatter ignore the log statements

A common problem is that formatters like prettier split up the log statements into multiple lines, making them hard to read and breaking .removeLogs(), which relies on each line containing the marker emoji.

The simplest method to deal with this is to customize the log statement in your configuration to include an ignore-comment: /* prettier-ignore */.

require("chainsaw").setup {
	logStatements = {
		variableLog = {
			javascript = {
				"/* prettier-ignore */ // {{marker}}", -- adding this
				'console.log("{{marker}} {{var}}:", {{var}});',
			},
		},
	},
}

Similar plugins

About the developer

In my day job, I am a sociologist studying the social mechanisms underlying the digital economy. For my PhD project, I investigate the governance of the app economy and how software ecosystems manage the tension between innovation and compatibility. If you are interested in this subject, feel free to get in touch.

I also occasionally blog about vim: Nano Tips for Vim

Buy Me a Coffee at ko-fi.com

Footnotes

  1. variableLog for nvim_lua uses a log statement that inspects objects and is designed to work with various notification plugins like nvim-notify, snacks.nvim, or noice.nvim. If using snacks.nvim, lua syntax highlighting is added as well. ↩

  2. Uses statements such as outline: 2px solid red !important; that are the somewhat similar logging. ↩

  3. The packages fmt and time need to be imported manually. ↩