Multifunctional IDE using Neovim (1 of 3)
Tags: neovim, javascript, rust, code
Published: 2024-06-14 12:49:29

## Introduction

Hello everyone, my dear programmers, today we will step by step make from the standard vim, a full-fledged tool capable of replacing VSCode if desired.

I often hear this question from my friends. Why not use a ready-made config like _LazyVim_ or _NVChad_? Well, firstly, I am the kind of person who likes to have control over all aspects of what I use. Secondly, when you say that you made your own config from scratch - it sounds cooler than just installing a couple of plugins (─‿‿─).

Can this replace _VSCode_? Maybe, as for me Vim is more convenient and faster to use than VSCode, but then again sometimes I also use _VSCode_, for some specific cases.

If you have any questions about the code, you can use [my repository](https://github.com/Alekanteri/dot-files-mac) as a reference.

## Configuration

First, you need to prepare your working environment (terminal) for correct work with vim. Preparation consists in the fact that you need to install fonts for displaying icons. Using [Nerd Fonts](https://www.nerdfonts.com/#home) download the fonts that you like best, but I recommend _JetBrainsMono_, I tried _Hack_, but it did not have all the icons, which is why I did not use it. Set the font in your terminal settings.

Next, install [NeoVim](https://neovim.io/) itself on your device via the official website, or if you have _homebrew_, you can install it via the appropriate command `brew install neovim`, _Linux_ users have their own package managers for installing packages, but I will not list them :)

And now we go to _.config_ directory and create here the new _nvim_ folder, this is where we will write all our code.

### Basic setup

And finally we can start writing the configuration, create the `init.lua` file in the _nvim_ folder. For convenience, we will split our config into different modules, but ultimately all modules will be imported into the `init.lua` file. For example, let's create a new file `default.lua`, in order for lua to recognize this file as a module we must create it in a folder called _lua_, otherwise it will not be visible to the main file when imported.
```
├── init.lua
└── lua
   └── default.lua
```
In this file we will write some basic commands:

```lua
--default.lua

vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.scrolloff = 5
vim.opt.sidescrolloff = 5
vim.opt.hlsearch = true
vim.opt.incsearch = true
vim.opt.tabstop = 2
vim.opt.shiftwidth = 2
vim.opt.expandtab = true
vim.opt.autoindent = true
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.swapfile = true
vim.opt.autoread = true
vim.opt.cursorline = true
vim.opt.termguicolors = true
vim.bo.autoread = true
vim.opt.clipboard = "unnamedplus"

vim.api.nvim_create_autocmd("TextYankPost", {
  callback = function()
    vim.highlight.on_yank({
      higroup = "incSearch",
      timeout = 100,
    })
  end,
})
```
To import this file we write this in init.lua:

```lua
--init.lua

require("default")

-- in the future I will not show that I import this or that file into init.lua, just know that if I write that a new file is created, then in most cases it will be imported into init.lua
```

Ok, let's go back to our `init.lua`, and install our first plugin, or rather the _lazy.nvim_ plugin manager, through it we will install all our plugins in the future.

{% embed https://github.com/folke/lazy.nvim %}


to install it you need to write the following piece of code:

```lua
--init.lua

local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
  vim.fn.system({
    "git",
    "clone",
    "--filter=blob:none",
    "https://github.com/folke/lazy.nvim.git",
    "--branch=stable",
    lazypath,
  })
end
vim.opt.rtp:prepend(lazypath)
```
For further installation of plugins, we add a structure from the plugin manager and inside it we will add new plugins:

```lua
--init.lua

require("lazy").setup({})
```

For example, let's set a color scheme, I like [TokyoNight](https://github.com/folke/tokyonight.nvim), you can set any you like:

```lua
--init.lua

require("lazy").setup({
---
  {
    "folke/tokyonight.nvim",
    lazy = false,
    priority = 1000,
    opts = {
      style = "night",
      styles = {
        comments = { italic = true },
      },
    },
  },
---
})

-- to apply the color scheme we write this
vim.cmd([[colorscheme tokyonight]])
```

Restart our _nvim_ and all done! <(￣︶￣)>

In _neovim_ you can set your own keymap, create a new file `keymap.lua`,  my standard layout looks like this:

```lua
--keymap.lua

local kmap = vim.keymap

vim.g.mapleader = " "

kmap.set("n", "<leader>q", "<cmd>q<cr>")
kmap.set("n", "<leader>w", "<cmd>w<cr>")

kmap.set("i", "jk", "<esc>")
kmap.set("n", "<leader>h", "<cmd>noh<cr>")
```

The next step is to install and configure the _Mason_ package manager, it  allows you to easily manage external editor tooling such as LSP servers, DAP servers, linters, and formatters through a single interface.

{% embed https://github.com/williamboman/mason.nvim %}

first of all, let's install it:

```lua
--init.lua

  require("lazy").setup({
---
  { "williamboman/mason.nvim" },
  { "williamboman/mason-lspconfig.nvim" },
  { "neovim/nvim-lspconfig" },
  { "WhoIsSethDaniel/mason-tool-installer.nvim" },
---
  })
```

Restart our `nvim` and it should install normally. Now let's write the configuration of the package manager itself. Create a new file `mason_config.lua` and here we write this:

```lua
---mason_config.lua

require("mason").setup({
	ui = {
		icons = {
			package_installed = "✓",
			package_pending = "➜",
			package_uninstalled = "✗",
		},
	},
})
require("mason-lspconfig").setup()
```

Okay, now let's set up our first language server, here you choose for which language you need to install a server, for example, I need servers for _html_, _css_, _typescript_, _rust_, _tailwind_ and _lua_, in addition I will install _emmet_ for faster work with html.

How do you find the configuration for your language? This package manager has a [List with all supported language servers](https://github.com/williamboman/mason-lspconfig.nvim/blob/main/doc/server-mapping.md), look for the language you need there, in nvim through the command `:Mason` look for the package name as in the table, and install, then in the next column open the link to the language server, and take the configuration from there. Let's install the language server for all languages that i use as an example

> For language servers sometimes you need to install additional plugins, for example, for _rust_ you need to install _rustaceanvim_, or for _TS_ - _typescript-tools_, and additionally a linter, so if you need to work with these languages, here are these plugins, for other languages ​​look in the repository link to which is above

```lua
--init.lua

  require("lazy").setup({
---
   {
    "mrcjkb/rustaceanvim",
    version = "^4", -- Recommended
    ft = { "rust" },
  },
  {
    "pmizio/typescript-tools.nvim",
    dependencies = { "nvim-lua/plenary.nvim", "neovim/nvim-lspconfig" },
  },
  {
    "mfussenegger/nvim-lint",
    linters_by_ft = {
      javascript = { "eslint_d" },
      javascriptreact = { "eslint_d" },
      typescript = { "eslint_d" },
      typescriptreact = { "eslint_d" },
    },
  },
---
  })
```

```lua
---mason_config.lua

---

require("mason-lspconfig").setup_handlers({
  function(server_name)
    require("lspconfig")[server_name].setup({})
  end,

  ["rust_analyzer"] = function()
    require("rustaceanvim").setup({})
  end,

  ["tailwindcss"] = function()
    require("lspconfig").tailwindcss.setup({})
  end,

  ["tsserver"] = function()
    require("typescript-tools").setup({})

    vim.keymap.set("n", "<leader>to", "<cmd>TSToolsOrganizeImports<cr>")
    vim.keymap.set("n", "<leader>tr", "<cmd>TSToolsRemoveUnusedImports<cr>")
    vim.keymap.set("n", "<leader>ta", "<cmd>TSToolsFixAll<cr>")

    local api = require("typescript-tools.api")
    require("typescript-tools").setup({
      handlers = {
        ["textDocument/publishDiagnostics"] = api.filter_diagnostics({ 6133 }),
      },
    })
  end,

  ["html"] = function()
    local capabilities = vim.lsp.protocol.make_client_capabilities()
    capabilities.textDocument.completion.completionItem.snippetSupport = true

    require("lspconfig").html.setup({
      capabilities = capabilities,
    })
  end,

  ["emmet_ls"] = function()
    require("lspconfig").emmet_ls.setup({})
  end,

  ["cssls"] = function()
    local capabilities = vim.lsp.protocol.make_client_capabilities()
    capabilities.textDocument.completion.completionItem.snippetSupport = true

    require("lspconfig").cssls.setup({
      capabilities = capabilities,
    })
  end,

  ["lua_ls"] = function()
    require("lspconfig").lua_ls.setup({
      on_init = function(client)
        local path = client.workspace_folders[1].name
        if not vim.loop.fs_stat(path .. "/.luarc.json") and not vim.loop.fs_stat(path .. "/.luarc.jsonc") then
          client.config.settings = vim.tbl_deep_extend("force", client.config.settings, {
            Lua = {
              diagnostics = {
                globals = { "vim" },
              },
              runtime = {
                version = "LuaJIT",
              },
              workspace = {
                checkThirdParty = false,
                library = {
                  vim.env.VIMRUNTIME,
                },
              },
            },
          })
        end
        return true
      end,
    })
  end,
})
```

The first language server is ready, congratulations. Let's now add formatting to our code so that it looks more structured. To do this, install it via _lazy.nvim_ and immediately write its config in a separate file `formatter.lua`

```lua
--init.lua

require("lazy").setup({
---

  {"stevearc/conform.nvim",},

---

})
```

Now we write its configuration, for this we need to install the corresponding formatters via _Mason_, for example _prettier_ for _JS/TS_ or _stylua_ for _lua_. Some language servers have ready-made formatters, for example _rust_analyzer_, so there is no need to configure code formatting for them. In our case, we need to install _stylua_ for lua, which is what we are doing.

```lua
--formatter.lua

require("conform").setup({
	formatters_by_ft = {
		lua = { "stylua" },
	},
	format_on_save = {
		timeout_ms = 500,
		lsp_fallback = true,
	},
})
```

Lastly for this chapter, let's set up autopairs so that when you type an opening parenthesis, a closing parenthesis is automatically created.

```lua
--init.lua

require("lazy").setup({
---

  {
    "windwp/nvim-autopairs",
    event = "InsertEnter",
    config = true,
  },

---

})
```

There is no need to configure anything here, everything works out of the box.