NeoVim飞升过程

为什么选择 Neovim

安装

Windows

macOS / OS X

Linux

Linux AppImage通用包

curl -LO https://github.com/neovim/neovim/releases/download/stable/nvim.appimage
chmod u+x nvim.appimage
./nvim.appimage

常见Linux发行版

yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
# python模块
yum install -y neovim python3-neovim
# 在较旧的版本上,可能需要python2-neovim
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:neovim-ppa/stable
sudo apt-get update
sudo apt-get install neovim

# python模块
sudo apt-get install python-dev python-pip python3-dev python3-pip
sudo apt-get install python-dev python-pip python3-dev
sudo apt-get install python3-setuptools
sudo easy_install3 pip

BSD (Unix)

更多产考官方wiki

Nvim 配置

参考 从零开始配置 Neovim(Nvim) - MartinLwx's Blog

在配置 Nvim 的时候,我们 尽可能用 Lua 语言写配置,因此有必要了解一下 Lua 的基本语法和语义。可以快速浏览一下 lua基础语法 了解大概

GitHub - rockerBOO/awesome-neovim: Neovim 插件集合

配置文件路径

Nvim 的配置目录在 ~/.config/nvim 下。在 Linux/Mac 系统上,Nvim 会默认读取 ~/.config/nvim/init.lua 文件,理论上来说可以将所有配置的东西都放在这个文件里面,但这样不是一个好的做法,因此我们划分不同的文件和目录来分管不同的配置

.config/nvim
├── init.lua
└── lua
    ├── config
    │   ├── keymaps.lua
    │   ├── lazy.lua
    │   └── options.lua
    └── plugins
        ├── nvim-cmp.lua
        ├── nvim-lspconfig.lua
        └── tokyonight.lua

选项配置

主要用到的就是 vim.gvim.optvim.cmd 等,快速参照对比的表格

In Vim In Nvim Note
let g:foo = bar vim.g.foo = bar
set foo = bar vim.opt.foo = bar set foo = vim.opt.foo = true
some_vimscript vim.cmd(some_vimscript)

按键配置

Nvim 里面进行按键绑定的语法如下,具体的解释可以看 :h vim.keymap.set

vim.keymap.set(<mode>, <key>, <action>, <opts>)

从零开始配置 Nvim

在安装完成之后,如果 ~/.config/nvim 目录不存在,创建目录并新建 init.lua 文件

mkdir ~/.config/nvim
mkdir ~/.config/nvim/lua
touch ~/.config/nvim/init.lua

配置文件编辑保存之后,重启 Nvim 就能看到效果,后面默认每次小章节配置完成后就重启

选项配置

选项配置功能一览:

新建 ~/.config/nvim/lua/config/options.lua 文件并加入如下内容

-- 提示:如果需要,使用 `:h <option>` 来了解含义
vim.opt.clipboard = 'unnamedplus' -- 使用系统剪贴板
vim.opt.completeopt = { 'menu', 'menuone', 'noselect' }
vim.opt.mouse = 'a' -- 允许在 Nvim 中使用鼠标

-- Tab
vim.opt.tabstop = 4 -- 每个 TAB 的视为的空格数
vim.opt.softtabstop = 4 -- 编辑时制表符的空格数
vim.opt.shiftwidth = 4 -- 插入 4 个空格作为一个制表符
vim.opt.expandtab = true -- 制表符用空格表示,主要因为 Python

-- UI 配置
vim.opt.number = true -- 显示绝对行号
vim.opt.relativenumber = true -- 在左侧添加每行的相对行号
vim.opt.cursorline = true -- 高亮光标所在行
vim.opt.splitbelow = true -- 新的竖向分割在底部打开
vim.opt.splitright = true -- 新的横向分割在右侧打开
-- vim.opt.termguicolors = true        -- 在 TUI 中启用 24 位 RGB 颜色
vim.opt.showmode = false -- 我们是经验丰富的用户,不需要 "-- INSERT --" 模式提示

-- 搜索
vim.opt.incsearch = true -- 输入字符时进行搜索
vim.opt.hlsearch = false -- 不高亮匹配项
vim.opt.ignorecase = true -- 默认情况下搜索时忽略大小写
vim.opt.smartcase = true -- 如果输入大写字母则变为区分大小写

然后打开 init.lua,用 require 导入刚才写的 options.lua 文件

require('config.options')

按键配置

按键功能一览:

新建 ~/.config/nvim/lua/config/keymaps.lua 文件并放入如下内容

-- 定义常用选项
local opts = {
    noremap = true,      -- 非递归
    silent = true,       -- 不显示消息
}

-----------------
-- 普通模式 --
-----------------

-- 提示:查看 `:h vim.map.set()`
-- 更好的窗口导航
vim.keymap.set('n', '<C-h>', '<C-w>h', opts)
vim.keymap.set('n', '<C-j>', '<C-w>j', opts)
vim.keymap.set('n', '<C-k>', '<C-w>k', opts)
vim.keymap.set('n', '<C-l>', '<C-w>l', opts)

-- 使用箭头调整大小
-- 增量:2 行
vim.keymap.set('n', '<C-Up>', ':resize -2<CR>', opts)
vim.keymap.set('n', '<C-Down>', ':resize +2<CR>', opts)
vim.keymap.set('n', '<C-Left>', ':vertical resize -2<CR>', opts)
vim.keymap.set('n', '<C-Right>', ':vertical resize +2<CR>', opts)

-----------------
-- 可视模式 --
-----------------

-- 提示:以与先前区域相同的区域和相同模式开始可视模式
vim.keymap.set('v', '<', '<gv', opts)
vim.keymap.set('v', '>', '>gv', opts)

然后在 init.lua 文件里面再次加上一行导入这个文件

require('config.keymaps')

安装插件管理器

一个强大的 Nvim 离不开插件的支持。我们选用的是当下最为流行 lazy.nvim。它支持如下许多特性:

-- 启动 lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
  local lazyrepo = "https://github.com/folke/lazy.nvim.git"
  local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
  if vim.v.shell_error ~= 0 then
    vim.api.nvim_echo({
      { "克隆 lazy.nvim 失败:\n", "ErrorMsg" },
      { out, "WarningMsg" },
      { "\n按任意键退出..." },
    }, true, {})
    vim.fn.getchar()
    os.exit(1)
  end
end
vim.opt.rtp:prepend(lazypath)

-- 确保在加载 lazy.nvim 之前设置 `mapleader` 和 `maplocalleader`
-- 以便映射正确。这也是设置其他选项(vim.opt)的好地方
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

-- 设置 lazy.nvim
require("lazy").setup({
  spec = {
    -- 导入~/.config/nvim/lua/plugins下的所有插件
    { import = "plugins" },
  },
  -- 在此配置任何其他设置。有关更多详细信息,请参阅文档。
  -- 安装插件时将使用的颜色方案。
  install = { colorscheme = { "habamax" } },
  -- 自动检查插件更新
  checker = { enabled = true },
})

lazy.nvim 指定第三方插件很简单,只需要在 require("lazy").setup({ ... })... 里面声明插件
然后在 init.lua 文件里面再次加上一行导入这个文件

require('config.lazy')

此时重启 Nvim 会自动安装 lazy.nvim ,静待片刻即可。等待 Dashboard 出现之后,输入 :Lazy 弹出了 lazy.nvim 的窗口,那就安装成功了

主题

使用 tokyonight 主题

-- ~/.config/nvim/lua/plugins/tokyonight.lua
return {
  "folke/tokyonight.nvim",
  config = function()
    require("tokyonight").setup({
      style = "night", -- 选择主题风格:moon, storm, night, 或者 day
      transparent = true, -- 启用透明背景
      terminal_colors = true, -- 使终端也使用主题的颜色
      styles = {
        comments = { italic = true },
        keywords = { italic = true },
        functions = {},
        variables = {},
        sidebars = "transparent", -- 让侧边栏透明
        floats = "transparent", -- 让浮动窗口透明
      },
    })
    -- 应用主题
    vim.cmd("colorscheme tokyonight")
  end,
}

使用 catppuccin 主题

-- ~/.config/nvim/lua/plugins/catppuccin.lua
return {
  "catppuccin/nvim",
  name = "catppuccin",
  config = function()
    require("catppuccin").setup({
      flavour = "mocha", -- 选择主题风格:latte, frappe, macchiato, mocha
      transparent_background = true, -- 启用透明背景
      term_colors = true, -- 使终端也使用主题的颜色
      styles = {
        comments = { "italic" },
        conditionals = { "italic" },
        loops = {},
        functions = {},
        keywords = {},
        strings = {},
        variables = {},
        numbers = {},
        booleans = {},
        properties = {},
        types = {},
        operators = {},
      },
      integrations = {
        nvimtree = {
          enabled = true,
          show_root = true,
          transparent_panel = true, -- 让NvimTree侧边栏透明
        },
        -- 在这里添加更多插件的集成配置
      },
    })
    -- 应用主题
    vim.cmd("colorscheme catppuccin")
  end,
}

自动补全

-- ~/.config/nvim/lua/plugins/nvim-cmp.lua
return {
	  "hrsh7th/nvim-cmp",
  dependencies = {
    "hrsh7th/cmp-buffer",
    "hrsh7th/cmp-path",
    "hrsh7th/cmp-cmdline",
    "hrsh7th/cmp-nvim-lsp",
    "saadparwaiz1/cmp_luasnip",
    "L3MON4D3/LuaSnip",
    "rafamadriz/friendly-snippets",
  },
  config = function()
    local cmp = require("cmp")
    local luasnip = require("luasnip")

    require("luasnip.loaders.from_vscode").lazy_load()

    cmp.setup({
      snippet = {
        expand = function(args)
          require("luasnip").lsp_expand(args.body)
        end,
      },
      mapping = cmp.mapping.preset.insert({
        ["<C-b>"] = cmp.mapping.scroll_docs(-4),
        ["<C-f>"] = cmp.mapping.scroll_docs(4),
        ["<C-Space>"] = cmp.mapping.complete(),
        ["<C-e>"] = cmp.mapping.abort(),
        ["<CR>"] = cmp.mapping.confirm({ select = true }),
        ["<Tab>"] = cmp.mapping(function(fallback)
          if cmp.visible() then
            cmp.select_next_item()
          elseif luasnip.expand_or_jumpable() then
            luasnip.expand_or_jump()
          else
            fallback()
          end
        end, { "i", "s" }),
        ["<S-Tab>"] = cmp.mapping(function(fallback)
          if cmp.visible() then
            cmp.select_prev_item()
          elseif luasnip.jumpable(-1) then
            luasnip.jump(-1)
          else
            fallback()
          end
        end, { "i", "s" }),
      }),
      sources = cmp.config.sources({
        { name = "nvim_lsp" },
        { name = "luasnip" },
      }, {
        { name = "buffer" },
        { name = "path" },
      }),
    })

    -- 对 `/` 使用缓冲区源(如果你启用了 `native_menu`,这将不再有效)。
    cmp.setup.cmdline("/", {
      mapping = cmp.mapping.preset.cmdline(),
      sources = {
        { name = "buffer" }
      }
    })

    -- 对 `:` 使用命令行和路径源(如果你启用了 `native_menu`,这将不再有效)。
    cmp.setup.cmdline(":", {
      mapping = cmp.mapping.preset.cmdline(),
      sources = cmp.config.sources({
        { name = "path" }
      }, {
        { name = "cmdline" }
      })
    })
  end,
}

LSP

-- ~/.config/nvim/lua/plugins/nvim-lspconfig.lua
return {
  "neovim/nvim-lspconfig",
  dependencies = {
    "hrsh7th/nvim-cmp",
    "hrsh7th/cmp-nvim-lsp",
    "williamboman/mason.nvim",
    "williamboman/mason-lspconfig.nvim",
  },
  config = function()
    local lspconfig = require("lspconfig")
    local cmp_nvim_lsp = require("cmp_nvim_lsp")
    local capabilities = cmp_nvim_lsp.default_capabilities(vim.lsp.protocol.make_client_capabilities())

    -- 确保服务器已安装
    require("mason").setup()
    require("mason-lspconfig").setup({
    -- 通过 :help lspconfig-all 可获取支持的LSP列表
    -- 详情 https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md
      ensure_installed = {
      -- 在这里添加你需要的 LSP
        "lua_ls", -- lua
        "pyright", -- python
        "rust_analyzer", -- rust
      },
    })
	
    -- 定义 `on_attach` 函数
    local on_attach = function(client, bufnr)
      local function buf_set_keymap(...) vim.api.nvim_buf_set_keymap(bufnr, ...) end
      local function buf_set_option(...) vim.api.nvim_buf_set_option(bufnr, ...) end

      buf_set_option("omnifunc", "v:lua.vim.lsp.omnifunc")

      -- Mappings.
      local opts = { noremap=true, silent=true }

      buf_set_keymap("n", "gD", "<Cmd>lua vim.lsp.buf.declaration()<CR>", opts)
      buf_set_keymap("n", "gd", "<Cmd>lua vim.lsp.buf.definition()<CR>", opts)
      buf_set_keymap("n", "K", "<Cmd>lua vim.lsp.buf.hover()<CR>", opts)
      buf_set_keymap("n", "gi", "<Cmd>lua vim.lsp.buf.implementation()<CR>", opts)
      buf_set_keymap("n", "<C-k>", "<Cmd>lua vim.lsp.buf.signature_help()<CR>", opts)
      buf_set_keymap("n", "<space>wa", "<Cmd>lua vim.lsp.buf.add_workspace_folder()<CR>", opts)
      buf_set_keymap("n", "<space>wr", "<Cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>", opts)
      buf_set_keymap("n", "<space>wl", "<Cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>", opts)
      buf_set_keymap("n", "<space>D", "<Cmd>lua vim.lsp.buf.type_definition()<CR>", opts)
      buf_set_keymap("n", "<space>rn", "<Cmd>lua vim.lsp.buf.rename()<CR>", opts)
      buf_set_keymap("n", "gr", "<Cmd>lua vim.lsp.buf.references()<CR>", opts)
      buf_set_keymap("n", "<space>e", "<Cmd>lua vim.lsp.diagnostic.show_line_diagnostics()<CR>", opts)
      buf_set_keymap("n", "[d", "<Cmd>lua vim.lsp.diagnostic.goto_prev()<CR>", opts)
      buf_set_keymap("n", "]d", "<Cmd>lua vim.lsp.diagnostic.goto_next()<CR>", opts)
      buf_set_keymap("n", "<space>q", "<Cmd>lua vim.lsp.diagnostic.set_loclist()<CR>", opts)
      buf_set_keymap("n", "<space>f", "<Cmd>lua vim.lsp.buf.formatting()<CR>", opts)
    end

    -- 设置 LSP 服务器
    require("mason-lspconfig").setup_handlers({
      function(server_name)
        lspconfig[server_name].setup({
          on_attach = on_attach,
          capabilities = capabilities,
        })
      end,
    })
  end,
}

状态栏

美化配置(Old)

若已经有vim的配置,可使用ln -s .vim/vimrc .config/nvim/init.vim与vim共用配置,可能有不兼容情况,尽量新建nvim ~/.config/nvim/init.vim

状态栏美化插件,需要安装powerline-fonts字体(https://github.com/powerline/fonts) pacman -S powerline-fonts