Skip to content

Customization

Shaobin-Jiang edited this page Aug 24, 2024 · 7 revisions

The best thing about neovim is how much it can be customized. Even here, with IceNvim, where so many things have been done for you already, you are still at liberty to customize almost anything to your own need.

Where to Put the Customization Files

First, find you got to find your config directory, which is ~/.config/nvim for unix systems and ~/AppData/Local/nvim for Windows system. This would be where you have git cloned the IceNvim repo and if you have done that already, you will see that there is a lua directory here, and under it are the core and plugins directories.

πŸ“‚ nvim
|-- πŸ“‚ after
|-- πŸ“‚ ftplugin
|-- πŸ“‚ lua
    |-- πŸ“‚ core
    |-- πŸ“‚ plugins
|-- πŸ“‚ screenshots
|-- πŸ“„ .gitignore
|-- πŸ“„ .stylua.toml
|-- πŸ“„ LICENSE
|-- πŸ“„ README.md
|-- πŸ“„ init.lua

IceNvim will be looking for a directory called custom under lua, and inside it, an init.lua file. The directory is not tracked by git and the init.lua file is loaded if present, which is how customization is enabled. So, if you want to enable customization, you should create the directory and the file, so your directory structure would be something like:

πŸ“‚ nvim
|-- πŸ“‚ after
|-- πŸ“‚ ftplugin
|-- πŸ“‚ lua
    |-- πŸ“‚ core
    |-- πŸ“‚ custom
        |-- πŸ“„ init.lua
        |-- πŸ“„ file-loaded-in-init.lua
    |-- πŸ“‚ plugins
|-- πŸ“‚ screenshots
|-- πŸ“„ .gitignore
|-- πŸ“„ .stylua.toml
|-- πŸ“„ LICENSE
|-- πŸ“„ README.md
|-- πŸ“„ init.lua

You can, as you can see from above, also add other files in the custom directory and load them in init.lua.

How Things Work

IceNvim loads its configuration file following this procedure: it loads the core module and the plugins module and store the values in a global table called Ice. What is loaded are things like keymaps and plugin configurations, but what is important is that, while I am using the word "load", they are actually only "stored" in the table. The keymaps are described but not yet set, and the plugins configuration are not yet accessed by the Lazy plugin manager (which means that up to now, the plugins are not loaded, something you ought to keep in mind when customizing later).

After this loading process, IceNvim goes on to loading the custom module. Since Ice now stores the default configuration and is globally accessible, you can now tweak with them however you want to. Also, while some settings done in core/basic.lua have not been stored in the table, you can also easily override them in your custom/init.lua file.

Afterwards, when your customization is finished, IceNvim uses the eventual Ice table for setting keymaps and loading the plugins.

Plugins

The most important thing you might want to know about customization is to add / remove a plugin or change its configuration. It is easy. All plugin configurations are stored in Ice.plugins. Each plugin is stored as a table that can be recognized by Lazy (see its documentation). Adding a plugin mostly involves adding a key that is not already in Ice.plugins and set its value to the proper table.

If you want to change how a plugin is loaded, you will need to look into how it is loaded by default by checking in lua/plugins/config.lua, but it would not be too hard.

If you want to remove a plugin, just set its value to nil.

For example

local view = Ice.plugins["nvim-tree"].opts.view
view.number = true
view.relativenumber = true

Ice.plugins.bufferline = nil

Lsp

Lsp-related configurations are stored in Ice.lsp. You can check out the defaults at lua/lsp/config.lua.

Ice.lsp is a table that maps a table to each server whose name you can find in mason, like css-lsp and lua-language-server. Each of these tables might contain these fields:

formatter

Set this if you want to associate a formatter with a particular lsp server; the formatter will be automatically set up by null-ls.

For example:

["json-lsp"] = {
    formatter = "prettier",
}

setup

This can either be a table that you use for nvim-lspconfig or a function that returns such a table. For example:

gopls = {
    formatter = "gofumpt",
    setup = {
        settings = {
            gopls = {
                analyses = {
                    unusedparams = true,
                },
            },
        },
    },
}

managed_by_plugin

This is a special field. You might notice that there are fields in the default setup whose name are not found in mason, like rust and flutter. These are handled not by the conventional way of setting up an lsp, but are rather dealt with by external plugins. For these languages, you might want to explicitly set managed_by_plugin to true, which is false by default.

You might wonder why this is necessary: why can we not just omit the table altogether? That is because we have one further field.

enabled

Another boolean value that indicates whether a language is enabled. The value is false by default. IceNvim has presets for many languages but people probably do not want to enable all of them, especially upon the first startup, as mason would then start to download a lot of things which is quite time-consuming. The only enabled language is lua, by the name of lua-language-server.

The existence of this field answers the question above: we need the managed_by_plugin field because we also need to specify whether we want these plugin-handled languages to be enabled. If IceNvim is not informed that a particular language should not be handled by nvim-lspconfig, it would just do the same setup for that language which naturally leads to errors.

Keymap

This part is a bit chaotic as IceNvim has placed the keymaps to where they should belong, namely by putting general plugins to the core module, plugin-related modules to plugin configurations and lsp-related plugins to the lsp part. Therefore, you might have to visit quite a lot of places to find all the keymaps.

Ice.keymap.general

This is a table that stores keymaps which do not require any plugin. The key is the desc of the keymap, and the value is a table that contains the mode, lhs, rhs and options. For example:

Ice.keymap.general.black_hole_register = { { "n", "v" }, "\\", '"_' }

Note that options can be omitted and rhs can be either a string or a function. The desc, i.e., the key here, will be the description in which-key.nvim.

Ice.keymap.plugins

Beware that, despite the name, this is where the keymaps for individual plugins are stored. Rather, these are keys mapped to functions not provided by the plugins themselves but which cannot function unless the plugins are installed. They follow the same format as Ice.keymap.general.

The keys Property in Plugin Configurations

Check the lazy.nvim documentation.

For example:

Ice.plugins.bufferline.keys = {
    { "<leader>bc", ":BufferLinePickClose<CR>", desc = "pick close", silent = true, noremap = true },
    { "<leader>bd", ":BufferLineClose 0<CR><ESC>", desc = "close current buffer", silent = true, noremap = true },
    { "<leader>bh", ":BufferLineCyclePrev<CR>", desc = "prev buffer", silent = true, noremap = true },
    { "<leader>bl", ":BufferLineCycleNext<CR>", desc = "next buffer", silent = true, noremap = true },
    { "<leader>bo", ":BufferLineCloseOthers<CR>", desc = "close others", silent = true, noremap = true },
    { "<leader>bp", ":BufferLinePick<CR>", desc = "pick buffer", silent = true, noremap = true },
}

Lsp-Related Keys

These are stored in Ice.lsp.keymap. Ice.lsp.keymap.mapLsp follows the same mapping rule as Ice.keymap.general, but Ice.lsp.keymap.cmp follows a different rule. That is, we are mapping keys to a short description of a command. The implementation of these short commands are handled elsewhere, and this allows you to have a easier way of changing the key bindings. The commands and their default key bindings are:

  • show_completion: <A-.>
  • hide_completion: <A-,>
  • prev_item: <C-k>
  • next_item: <C-j>
  • confirm: <Tab>
  • doc_up: <C-u>
  • doc_down: <C-d>

Colorscheme

You can add a colorscheme by adding a field into Ice.colorschemes. The value of that field would be a table that contains:

  • name: the name of the colorscheme plugin
  • background: either "light" or "dark"
  • setup (optional): a function executed when setting the colorscheme

Note that the colorscheme has to be installed in Ice.plugins first!

You can also set a colorscheme in Ice.colorscheme (notice the missing "s" compared to above). The value has to be a key in Ice.colorschemes. This way of setting the colorscheme has the highest priority. Without setting the value, the colorscheme picker provided by IceNvim would memorize your selected colorscheme, but once you set this value, whichever colorscheme you have picked in the colorscheme picker would be overridden in the next launch of neovim.

Misc

There also other keys in Ice already taken. For now, they include:

Ice.auto_chdir

Whether to enable automatic root directory switching. The default value is true.

Ice.chdir_root_pattern

A table containing patterns that determine a root directory. The default pattern is:

{
    ".git",
    "package.json",
    ".prettierrc",
    "tsconfig.json",
    "pubspec.yaml",
    ".gitignore",
    "stylua.toml",
    "README.md",
}

Ice.chdir_exclude_filetype

A table containing the file types not to enable automatic root directory switching for. The default value is:

{ "NvimTree", "help" }

Ice.chdir_exclude_buftype

A table containing the buffer types not to enable automatic root directory switching for. The default value is:

{ "terminal", "nofile" }

Ice.ft

A table that defines what to do when a specific filetype is detected. For example, if you want to set colorcolumn to 120, you can do this:

Ice.ft.javascript = function()
    vim.wo.colorcolumn = "120"
end

If you want to disable colorcolumn in neogit, you can do this:

Ice.ft["Neogit*"] = function()
    vim.wo.colorcolumn = ""
end

Some filetypes have default callbacks. If you want to add your own callback to them instead of just overriding them, you can use the Ice.ft:set() method:

Ice.ft:set("typst", function ()
    vim.wo.colorcolumn = "120"
end)

You can use the Ice.ft:set() method on filetypes that do not have default values as well. Beware that it is :set() instead of .set() though.

Ice.symbols

A table that defines the nerd font icons for such symbols as function / number / object / string / ...

lua/custom is Included in runtimepath

Neovim looks for such folders as queries, ftplugin, spell, etc., in your runtimepath. Typically, if one wishes to override the default treesitter queries or ftplugins or other things already set up by neovim, they would need to create the corresponding directory under their config directory.

However, that would prove inconvenient for IceNvim users as these new files would then be tracked by git and cause a lot of trouble. So the better way is to place the folders under lua/custom as well. For example, where you should have ~/.config/nvim/queries/markdown/highlights.scm, with IceNvim, it should be placed at ~/.config/nvim/queries/lua/custom/queries/markdown/highlights.scm:

πŸ“‚ nvim
|-- πŸ“‚ after
|-- πŸ“‚ ftplugin
|-- πŸ“‚ lua
    |-- πŸ“‚ core
    |-- πŸ“‚ custom
        |-- πŸ“„ init.lua
        |-- πŸ“„ file-loaded-in-init.lua
        |-- πŸ“‚ queries
            |-- πŸ“‚ markdown
                |-- πŸ“„ highlights.scm
    |-- πŸ“‚ plugins
|-- πŸ“‚ screenshots
|-- πŸ“„ .gitignore
|-- πŸ“„ .stylua.toml
|-- πŸ“„ LICENSE
|-- πŸ“„ README.md
|-- πŸ“„ init.lua