This lightweight collection of treesitter tools for Julia currently focuses just on violations of naming conventions.
It makes a best-effort attempt to parse your code and check that modules, types, constants, and functions adhere to your preferred case conventions. It's smart enough -- since treesitter doesn't give us semantic information -- to identify all type definitions first, which helps it avoid incorrectly flagging constructors as function-naming violations. So far this all works reasonably well, although there are of course cases that are out of scope for purely syntactic analysis.
- Targeted naming convention checks for Modules, Structs, Abstract Types, Constants, and Functions.
- Choose to lint the current buffer, the entire project, or specific directories.
- Configurable naming rules for compliance with your preferred style
- Reporting modes:
quickfixto send all violations to the quickfix list for review orjumpto jump to the first violation and then cycle through the rest.
Here's a brief demo checking the ForwardDiff.jl package (with plugin options set to the naming convention ForwardDiff.jl uses), then monkeying around in it to generate some violations:
nameStyles.mp4
Notice that in the above video when we change Dual to dual the previously unreported use of the constructors throws a potential flag.
Here's another example taking a look at JuMP.jl, using the jump mode rather than the quickfix list (using the same naming convention request as before, which may or may not be what the JuMP.jl team wants).
nameStyles2.mp4
This also highlights some of the limitations of syntactic rather than semantic analysis. For instance, in the macro at the beginning of the demo, the analysis correctly identifies that consts should be fixed, but it incorrectly flags the loop variable as a violation instead of the collection it's iterating over.
- Neovim >= 0.11 (for the modern treesitter query API)
- nvim-treesitter/nvim-treesitter
Install with your favorite plugin manager. Here's the minimal lazy.nvim installation:
{
"DanielMSussman/simpleJuliaTreesitterTools.nvim",
dependencies = { 'nvim-treesitter/nvim-treesitter'},
ft = "julia",
}Here is a more complete configuration example showing all of the options and some recommended keymaps:
{
"DanielMSussman/simpleJuliaTreesitterTools.nvim",
dependencies = { 'nvim-treesitter/nvim-treesitter'},
ft = "julia",
config = function()
require("simpleJuliaTreesitterTools").setup({
rules = {
["Constant"] = "SCREAMING_SNAKE_CASE",
["Module"] = "UpperCamelCase",
["Struct"] = "UpperCamelCase",
["AbstractType"] = "AbstractUpperCamelCase",
["Function"] = "snake_case",
},
defaultApproach = "treesitter",
lint_action = "quickfix", -- "quickfix" or "jump"
projectRootFile = "Project.toml",
projectDirectory = "/src",
})
vim.keymap.set('n', '<localleader>lb', function()
require('simpleJuliaTreesitterTools').lint_buffer_names()
end, { desc = '[L]int names in current [b]uffer' })
vim.keymap.set('n', '<localleader>lp', function()
require('simpleJuliaTreesitterTools').lint_project_names()
end, { desc = '[L]int names in current [p]roject' })
vim.keymap.set('n', '<localleader>lc', function()
require('simpleJuliaTreesitterTools').cycle_violations()
end, { desc = '[L]inting: [c]ycle to next violation' })
end
}The plugin provides three functions you can call or map to keys.
require('simpleJuliaTreesitterTools').lint_buffer_names()finds naming violations in the current bufferrequire('simpleJuliaTreesitterTools').lint_project_names()finds theprojectRootFileand lints all.jlfiles in theprojectDirectory.require('simpleJuliaTreesitterTools').cycle_violations(): whenlint_action = "jump", this command will move the cursor to the next naming violation in the list.
- Explore other treesitter-based actions I might want to do in Julia
- Investigate adding support for linting local variable names.
- There is also a half-hearted experimental implementation that tries to interface with the LanguageServer.jl LSP for everything; this works poorly (if at all) -- I was surprised that LSP-based enforcement wasn't already an option, but I'm sure a smarter, functional implementation is out there somewhere.
If, for some unexpected reason, you found this helpful and would like to offer support: Buy Me a Coffee!