Skip to content

📦 Practicalli AstroNvim Config Designλ︎

A guide to the AstroNvim Config user configuration created by Practicalli to support Clojure development.

AstroCommunity used where possible

Plugins and configuration is added vial AstroCommunity were possible, to minimise the code size and maintenance of the configuration

User Config overviewλ︎

core.lua is for tuning plugins shipped with astronvim config

plugins/ for additional plugins organised logically. All .lua files are read from this directory

  • user.lua for general user defined plugins
  • clojure.lua adds Conjure and parinf, ensuring Clojure treesitter parser and Clojure LSP

Clojure supportλ︎

The AstroCommunity provides a Clojure language pack that adds Conjure and nvim-parinfer, along with clojure Treesitter parser and clojure-lsp support.

Edit the plugins/community.lua file and import the Clojure pack. The "AstroNvim/astrocommunity", repository is already added to to the file.

-- Packs
-- Treesitter: clojure , Lsp: clojure-lsp, Lint/format:
{ import = "astrocommunity.pack.clojure" },
Override AstroCommunity Pack

Create a plugins/clojure.lua file and add the AstroCommunity repository, Clojure pack and additional configuration to your own preferences

Clojure configuration with user configration overrides

return {
  "AstroNvim/astrocommunity",
  { import = "astrocommunity.pack.clojure" },
  {
    "Olical/conjure",
    -- load plugin on filetypes
    ft = { "clojure", "fennel" },
    config = function()
      -- HUD
      -- Example: Set to `"SE"` and HUD width to `1.0` for full width HUD at bottom of screen
      vim.g["conjure#log#hud#width"] = 1 -- Width of HUD as percentage of the editor width, 0.0 and 1.0.
      vim.g["conjure#log#hud#enabled"] = false -- Display HUD
      vim.g["conjure#log#hud#anchor"] = "SE" -- Preferred corner position for the HUD
      vim.g["conjure#log#botright"] = true -- Open log at bottom or far right of editor
      -- REPL
      vim.g["conjure#extract#context_header_lines"] = 100 -- Number of lines to check for `ns` form
      vim.g["conjure#client#clojure#nrepl#connection#auto_repl#enabled"] = false -- ;; Start "auto-repl" process, eg. babashka
      vim.g["conjure#client#clojure#nrepl#connection#auto_repl#hidden"] = true -- ;; Hide auto-repl buffer when triggered
      vim.g["conjure#client#clojure#nrepl#connection#auto_repl#cmd"] = nil -- ;; Command to start the auto-repl
      -- ;; Automatically require namespace of new buffer or current buffer after connection
      vim.g["conjure#client#clojure#nrepl#eval#auto_require"] = false
      -- Reloading code
      -- Function to call on refresh (reloading) the log, namespace-qualified name of a zero-arity
      -- vim.g["conjure#client#clojure#nrepl#refresh#after"] = nil
      -- The namespace-qualified name of a zero-arity function to call before reloading.
      -- vim.g["conjure#client#clojure#nrepl#refresh#before"] = nil
      -- List of directories to scan. If no directories given, defaults to all directories on the classpath.
      -- vim.g["conjure#client#clojure#nrepl#refresh#dirs"] = nil
      -- Testing
      -- ;; Test runner called from the test key mappings
      vim.g["conjure#client#clojure#nrepl#test#runner"] = "kaocha"
      -- Print raw test evaluation result, suppressing prefix for stdout lines `; (out)`
      -- vim.g["conjure#client#clojure#nrepl#test#raw_out"] = nil
      -- Override string appended to the end of the test runner calls
      -- vim.g["conjure#client#clojure#nrepl#test#call_suffix"] = nil
    end
  },
  {
    "gpanders/nvim-parinfer",
    ft = { "clojure" },
    config = function()
      vim.g.parinfer_force_balance = true
      vim.g.parinfer_comment_chars = ";;"
    end,
  },
}

Add Conjure and parinfer plugin that will load when Clojure or Fennel file is opened.

Clojure Packages in AstroNvim user configuration

```lua title=".config/astronvim-config/plugins/clojure.lua"
-- Lazy Package manager configuration
return {
  {
    "Olical/conjure",
    -- load plugin on filetypes
    ft = { "clojure", "fennel" },
  },

  {
    "gpanders/nvim-parinfer",
    ft = { "clojure", "fennel" },
    config = function()
      vim.g.parinfer_force_balance = true
      vim.g.parinfer_comment_chars = ";;"
    end,
  },
}
```

Improve syntax highlighting by installing the Clojure parser for Treesitter.

Treesitter Parser for clojure in AstroNvim user configuration

.config/astronvim-config/plugins/treesitter.lua
return {
  "nvim-treesitter/nvim-treesitter",
  opts = function(_, opts)
    -- add more things to the ensure_installed table protecting against community packs modifying it
    opts.ensure_installed = require("astronvim.utils").list_insert_unique(opts.ensure_installed, {
      -- "lua"
    "clojure"
    })
  end,
}

Install Treesitter Clojure Parser manually

:TSInstall clojure in Neovim will install the parser. A parser not included in the opts.ensure_installed configuration must be updated manually each time treesitter plugin is updated

Switch to Parinfer Parens mode for Paredit structural editing

Changing the Parinfer mode to paren gives a structured editing experience similar to Paredit (or Smartparens).

Add the following configuration within the return {} table in the plugins/community.lua file to set the parinfef mode, i.e. paren, smart or indent (default

plugins/community.lua
  {
    "gpanders/nvim-parinfer",
    ft = { "clojure" },
    config = function()
      vim.g.parinfer_force_balance = true
      vim.g.parinfer_comment_chars = ";;"
      vim.g.parinfer_mode = "paren"
    end,
  },

Clojure Mappingsλ︎

Conjure mappings are defined respective to a <localleader> value. Define a local leader in the AstroNvim user configuration, e.g. , and all Conjure mappings become available.

AstroNvim 3.17.0 has localleader

AstroNvim 3.17.0 release sets localleader to , so a separate setting is not required in the user configuration (unless a different localleader is preferred)

Set localleader in user config

options.lua in the user configuration provides a consistent way to set Neovim options.

.config/astronvim-config/options.lua
-- set vim options here (vim.<first_key>.<second_key> = value)
return {
  opt = {
    -- set to true or false etc.
    relativenumber = true, -- sets vim.opt.relativenumber
    number = true,         -- sets vim.opt.number
    spell = false,         -- sets vim.opt.spell
    signcolumn = "auto",   -- sets vim.opt.signcolumn to auto
    wrap = false,          -- sets vim.opt.wrap
  },
  g = {
    mapleader = " ",                 -- sets vim.g.mapleader
    maplocalleader = ",",            -- Set local leader key binding (supports Conjure key bindings)
    autoformat_enabled = true,       -- enable or disable auto formatting at start (lsp.formatting.format_on_save must be enabled)
    cmp_enabled = true,              -- enable completion at start
    autopairs_enabled = true,        -- enable autopairs at start
    diagnostics_mode = 3,            -- set the visibility of diagnostics in the UI (0=off, 1=only show in status line, 2=virtual text off, 3=all on)
    icons_enabled = true,            -- disable icons in the UI (disable if no nerd font is available, requires :PackerSync after changing)
    ui_notifications_enabled = true, -- disable notifications when toggling UI elements
    VM_leader = "gm"                 -- Visual Multi Leader (multiple cursors)
  },
}

Clojure LSPλ︎

Clojure LSP support is enabled via the AstroCommunity Clojure pack.

clojure_lsp can be added using Mason UI, SPC p m or in the plugins/mason.lua file

Manual user config of clojure lsp server
-- customize mason plugins
return {
  -- use mason-lspconfig to configure LSP installations
  {
    "williamboman/mason-lspconfig.nvim",
    -- overrides `require("mason-lspconfig").setup(...)`
    opts = function(_, opts)
      -- add more things to the ensure_installed table protecting against community packs modifying it
      opts.ensure_installed = require("astronvim.utils").list_insert_unique(opts.ensure_installed, {
        -- "clojure_lsp",  -- provide by Clojure pack
        "marksman", -- Markdown structure (also in markdown pack)
        "yamlls",
      })
    end,
  },
}

Snippetsλ︎

The AstroNvim user example includes a commented LuaSnip configuration

.config/astronvim-config/plugins/core.lua
  -- {
  --   "L3MON4D3/LuaSnip",
  --   config = function(plugin, opts)
  --     require "plugins.configs.luasnip" (plugin, opts)  -- include the default astronvim config that calls the setup call
  --     -- add more custom luasnip configuration such as filetype extend or custom snippets
  --     local luasnip = require "luasnip"
  --     luasnip.filetype_extend("javascript", { "javascriptreact" })
  --   end,
  -- },

AstroNvim includes a Recipe for custom snippets

return {
  plugins = {
    {
      "L3MON4D3/LuaSnip",
      config = function(plugin, opts)
        require "plugins.configs.luasnip"(plugin, opts) -- include the default astronvim config that calls the setup call
        require("luasnip.loaders.from_vscode").lazy_load { paths = { "./lua/user/snippets" } } -- load snippets paths
      end,
    },
  },
}

Practicalli AstroNvim Config combines the two examples to get

AstroNvim config with custom VS Code style snippets

.config/astronvim-config/plugins/core.lua
{
  "L3MON4D3/LuaSnip",
  config = function(plugin, opts)
    require "plugins.configs.luasnip" (plugin, opts) -- include the default astronvim config that calls the setup call
    -- add more custom luasnip configuration such as filetype extend or custom snippets
    require("luasnip.loaders.from_vscode").lazy_load { paths = { "./lua/user/snippets" } } -- load snippets paths
    local luasnip = require "luasnip"
    luasnip.filetype_extend("javascript", { "javascriptreact" })
end,
},

AstroNvim Community packagesλ︎

🌐 AstroNvim Community provides a large number of packages currated by the community.

Visit the AstroNvim Community repository on GitHub and browse the packages available.

import each package of interest to the plugins/community.lua file in the AstroNvim user configuration.

AstroNvim Community Packages in AstroNvim user configuration

.config/astronvim-config/plugins/community.lua
return {
  -- Add the community repository of plugin specifications
  "AstroNvim/astrocommunity",
  -- Import each plugin from the Astro Community as required
  { import = "astrocommunity.editing-support.todo-comments" },
  { import = "astrocommunity.git.neogit" },
  { import = "astrocommunity.git.octo" },
  { import = "astrocommunity.git.openingh" },
}

AstroCommunity packs set up support for each language

Language packs enabled in Practicalli AstroNvim Config

.config/astronvim-config/plugin/community.lua
  -- Packs
  -- Treesitter: dockerfile , Lsp: dockerls & docker_compose_language_service, Lint/format: hadolint
  { import = "astrocommunity.pack.docker" },
  -- Treesitter: json & jsonc, Lsp: jsonls, Lint/format: stylua
  { import = "astrocommunity.pack.json" },
  -- Treesitter: lua, Lsp: lua_ls, Lint/format: stylua
  { import = "astrocommunity.pack.lua" },
  -- Treesitter: markdown & markdown_inline, Lsp: marksman, Lint/format: prettierd
  -- Pack disabled as prettierd too agressive with format
  -- { import = "astrocommunity.pack.markdown" },
  -- Treesitter: markdown & markdown_inline, Lsp: marksman, Lint/format: prettierd
  { import = "astrocommunity.pack.yaml" },

Themesλ︎

Themes are a collection of one or more colorschemes to affect the apperance of text, icons, highlights, etc.

Themes supporting vim.opt.background can change between dark and light colorscheme (SPC u b UI > background in AstroNvim)

SPC f t selector shows themes colorschemes, as long as the themes are configured to disable lazy loading

The default astrodark theme is set via the colorscheme option in init.lua

Everforest provides a good dark and light theme and supports the background option to toggle between each colorscheme.

Practicalli AstroNvim Config - default theme

colorscheme = "everforest",

AstroCommunity themes

Practicalli AstroNvim Config themes

return {
{
"AstroNvim/astrotheme", -- default AstroNvim theme
lazy = false,
},
  -- Add the community repository of plugin specifications
  "AstroNvim/astrocommunity",
  { import = "astrocommunity.colorscheme.everforest" },
  {
    "sainnhe/everforest",
    lazy = false,
  },
  { import = "astrocommunity.colorscheme.nightfox-nvim" },
  {
    "EdenEast/nightfox.nvim",
    lazy = false,
  },
  { import = "astrocommunity.colorscheme.kanagawa-nvim" },
  {
    "rebelot/kanagawa.nvim",
    lazy = false,
  },
  { import = "astrocommunity.colorscheme.github-nvim-theme" }, -- no background support
  {
    "projekt0n/github-nvim-theme",
    lazy = false,
  },

Configure Lazy pluginsλ︎

🌐 Lazy.nvim Plugin specification

Config Format and Lint toolsλ︎

Disable format on save when tools provide unexpected results

SPC u f toggles if the respective format tool should run for the current buffer. SPC u F for all buffers of the current kind.

init.lua lsp section can enable or disable format on save for specific file types.

Mason is responsible for installing lint and format tools

null-ls is responsible for running each tool and provides default configuration for code_actions, completion, diagnostics, formatting and hover.

🌐 null-ls built-in configuration

Override config file unconsistent

The configuration file defined by -config-path does not always seem to be used when running astronvim. Quit and start Neovim again seems to use the configuration file.

Override null-ls builtin configuration

Specify configuration files to use that override the null-ls builtin configuration

return {
  "jose-elias-alvarez/null-ls.nvim",
  opts = function(_, config)
    -- config variable is the default configuration table for the setup function call
    local null_ls = require "null-ls"
    config.sources = {
      null_ls.builtins.formatting.markdownlint.with {
        -- pass arguments to modify/override the null-ls builtin configuration
        extra_args = { 
          "--config-path", 
          vim.fn.expand("~/.config/astro-config/tool-config/markdownlint.yaml") },
      },
    }
    return config -- return final config table
  end,
}

vim.fn.expand() reports luacheck error accessing undefined variable but seems to work regardless

General configuration for LSP Servers
.config/astronvim-config/init.lua
  lsp = {
    -- customize lsp formatting options
    formatting = {
      -- control auto formatting on save
      format_on_save = {
        enabled = true,     -- format on save globally
        allow_filetypes = { -- format on save for specified filetypes only
          -- "go",
        },
        ignore_filetypes = { -- turn off format on save for specified filetypes
          -- "python",
        },
      },
      disabled = { -- switch off formatting capabilities for the listed language servers
        -- turn off lua_ls formatting capability if you want to use StyLua to format your lua code
        -- "lua_ls",
        "markdownlint",
      },
      timeout_ms = 1000, -- default format timeout
      -- filter = function(client) -- fully override the default formatting function
      --   return true
      -- end
    },
    -- enable servers that you already have installed without mason
    servers = {
      -- "pyright"
    },
  },

Override Key bindingλ︎

AstroNvim uses Lazy package manager to set keys for packages.

Astrocommunity configuration defines a keys table that is used by Lazy.

In the user configuration, return a function that sets key bindings to overide the keys table provided by astrocommunity

Override Key bindings for vim highlighter
.config/astronvim-config/plugins/community.lua
{
    "vim-highlighter",
    keys = function() 
        return {
            { "<leader>nn", "<cmd>Hi><CR>", desc = "Next Recently Set Highlight" },
            { "<leader>ng", "<cmd>Hi<<CR>", desc = "Previous Recently Set Highlight" },
            { "<leader>n[", "<cmd>Hi{<CR>", desc = "Next Nearest Highlight" },
            { "<leader>n]", "<cmd>Hi}<CR>", desc = "Previous Nearest Highlight" },
        }
    end,
}

Plugin Key bindingλ︎

Add key binding if a plugin is available wrapped in an if statement, when defining keys in a different place to adding the plugin, e.g whichkey mappings.lua

if is_available "plugin-name" then
  ,,,
else