Neovim LSP 将取代 VSCode
如果您正在寻找一种更简单的解决方案来用终端编辑器替换 VSCode,我建议您阅读我的极简 vim帖子。
TLDR;
所有代码均可从nvim-code获取
设置
本文假设 neovim 0.6+ 已安装并准备就绪。接下来,我们来规划一下接下来要处理的目录结构。
Neovim 默认使用${HOME}/.config/nvim
作为配置目录。我将以此为基础构建配置文件。
nvim
├── after
│ └── ftplugin
│ └── python.lua
├── init.lua
├── lua
│ ├── _lsp.lua
│ ├── _options.lua
│ ├── _plugins.lua
│ ├── _statusline.lua
│ ├── _telescope.lua
│ ├── _treesitter.lua
│ └── _whichkey.lua
初始化lua
require("_options")
require("_plugins")
require("_lsp")
require("_treesitter")
require("_telescope")
require("_whichkey")
require("_statusline")
vim.cmd("colorscheme walh-gruvbox")
_options.lua
我不会介绍所有可用的选项,但这里列出了一些可以使用或省略的常用选项。这很大程度上受到了lunarvim的启发。
vim.g.mapleader = " "
vim.g.border_style = "rounded"
vim.g.markdown_fenced_languages = {
"bash=sh",
}
vim.opt.backup = false -- creates a backup file
vim.opt.clipboard = "" -- don't use clipboard
vim.opt.cmdheight = 1 -- more space in the neovim command line for displaying messages
vim.opt.colorcolumn = "99999" -- fixes indentline for now
vim.opt.completeopt = { "menuone", "noselect" }
vim.opt.conceallevel = 0 -- so that `` is visible in markdown files
vim.opt.cursorline = true -- highlight the current line
vim.opt.expandtab = true -- convert tabs to spaces
vim.opt.fileencoding = "utf-8" -- the encoding written to a file
vim.opt.foldexpr = "" -- set to "nvim_treesitter#foldexpr()" for treesitter based folding
vim.opt.foldmethod = "manual" -- folding set to "expr" for treesitter based folding
vim.opt.hidden = true -- required to keep multiple buffers and open multiple buffers
vim.opt.hlsearch = true -- highlight all matches on previous search pattern
vim.opt.ignorecase = true -- ignore case in search patterns
vim.opt.list = true
vim.opt.listchars = "tab:│ ,trail:·,nbsp:+"
vim.opt.number = true -- set numbered lines
vim.opt.numberwidth = 1 -- set number column width to 2 {default 4}
vim.opt.pumheight = 10 -- pop up menu height
vim.opt.relativenumber = false -- set relative numbered lines
vim.opt.scrolloff = 4 -- is one of my fav
vim.opt.shiftwidth = 2 -- the number of spaces inserted for each indentation
vim.opt.showmode = false -- we don't need to see things like -- INSERT -- anymore
vim.opt.sidescrolloff = 4
vim.opt.signcolumn = "yes" -- always show the sign column otherwise it would shift the text each time
vim.opt.smartcase = true -- smart case
vim.opt.smartindent = true -- make indenting smarter again
vim.opt.spell = false -- disable spell checking
vim.opt.spelllang = "en" -- language for spell checking
vim.opt.splitbelow = true -- force all horizontal splits to go below current window
vim.opt.splitright = true -- force all vertical splits to go to the right of current window
vim.opt.swapfile = false -- creates a swapfile
vim.opt.tabstop = 2 -- insert 2 spaces for a tab
vim.opt.termguicolors = false -- set term gui colors (most terminals support this)
vim.opt.timeoutlen = 500 -- timeout length
vim.opt.title = true -- set the title of window to the value of the titlestring
vim.opt.titlestring = "%<%F - nvim" -- what the title of the window will be set to
vim.opt.undodir = vim.fn.stdpath("cache") .. "/undo"
vim.opt.undofile = true -- enable persistent undo
vim.opt.updatetime = 300 -- faster completion
vim.opt.wrap = true -- display lines as one long line
vim.opt.writebackup = false -- if a file is being edited by another program (or was written to file while editing with another program) it is not allowed to be edited
vim.opt.showtabline = 2 -- always show tabs
vim.opt.laststatus = 2 -- hide statusline
_plugins.lua
我不得不承认使用 neovims 内置 lsp 很好,但它需要安装许多插件才能获得与 VSCode 类似的体验。
local fn = vim.fn
local install_path = fn.stdpath("data") .. "/site/pack/packer/start/packer.nvim"
if fn.empty(fn.glob(install_path)) > 0 then
packer_bootstrap = fn.system({
"git",
"clone",
"--depth",
"1",
"https://github.com/wbthomason/packer.nvim",
install_path,
})
end
return require("packer").startup(function()
use({
"L3MON4D3/LuaSnip",
"casonadams/walh",
"folke/trouble.nvim",
"hrsh7th/cmp-buffer",
"hrsh7th/cmp-cmdline",
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/cmp-path",
"hrsh7th/nvim-cmp",
"jose-elias-alvarez/null-ls.nvim",
"neovim/nvim-lspconfig",
"nvim-lua/lsp-status.nvim",
"nvim-treesitter/nvim-treesitter",
"saadparwaiz1/cmp_luasnip",
"tamago324/nlsp-settings.nvim",
"wbthomason/packer.nvim",
"williamboman/nvim-lsp-installer",
})
use({
"rafamadriz/friendly-snippets",
})
use({
"nvim-telescope/telescope.nvim",
requires = { "nvim-lua/plenary.nvim" },
})
use({
"nvim-lualine/lualine.nvim",
requires = { "kyazdani42/nvim-web-devicons", opt = true },
})
use({
"folke/which-key.nvim",
config = function()
require("which-key").setup({})
end,
})
use({
"terrortylor/nvim-comment",
config = function()
require("nvim_comment").setup({})
end,
})
use({
"lewis6991/gitsigns.nvim",
config = function()
require("gitsigns").setup({ yadm = { enable = true } })
end,
})
use({
"ethanholz/nvim-lastplace",
event = "BufRead",
config = function()
require("nvim-lastplace").setup({
lastplace_ignore_buftype = { "quickfix", "nofile", "help" },
lastplace_ignore_filetype = { "gitcommit", "gitrebase", "svn", "hgcommit" },
lastplace_open_folds = true,
})
end,
})
-- Automatically set up your configuration after cloning packer.nvim
-- Put this at the end after all plugins
if packer_bootstrap then
require("packer").sync()
end
end)
_lsp.lua
local cmp = require("cmp")
local lsp_status = require("lsp-status")
local win = require("lspconfig.ui.windows")
local _default_opts = win.default_opts
win.default_opts = function(options)
local opts = _default_opts(options)
opts.border = "rounded"
return opts
end
-- statusline progress setup
lsp_status.config({
current_function = false,
show_filename = false,
diagnostics = false,
status_symbol = "",
select_symbol = nil,
update_interval = 200,
})
-- completion setup
cmp.setup({
snippet = {
expand = function(args)
-- vim.fn["vsnip#anonymous"](args.body)
require("luasnip").lsp_expand(args.body) -- For `luasnip` users.
-- vim.fn["UltiSnips#Anon"](args.body)
end,
},
mapping = {
["<C-d>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
["<C-Space>"] = cmp.mapping.complete(),
["<C-e>"] = cmp.mapping.close(),
["<CR>"] = cmp.mapping.confirm({ select = false }),
["<Tab>"] = cmp.mapping(cmp.mapping.select_next_item(), { "i", "s" }),
["<S-Tab>"] = cmp.mapping(cmp.mapping.select_prev_item(), { "i", "s" }),
},
sources = {
{ name = "nvim_lsp" },
{ name = "luasnip" },
-- { name = "ultisnips" },
-- { name = "vsnip" },
{ name = "buffer" },
{ name = "path" },
},
})
-- helper function for mappings
local m = function(mode, key, result)
vim.api.nvim_buf_set_keymap(0, mode, key, "<cmd> " .. result .. "<cr>", {
noremap = true,
silent = true,
})
end
-- function to attach completion when setting up lsp
local on_attach = function(client)
lsp_status.register_progress()
lsp_status.on_attach(client)
-- Mappings.
m("n", "ga", "lua vim.lsp.buf.code_action()")
m("n", "gD", "lua vim.lsp.buf.declaration()")
m("n", "gd", "lua vim.lsp.buf.definition()")
m("n", "ge", "lua vim.lsp.diagnostic.goto_next()")
m("n", "gE", "lua vim.lsp.diagnostic.goto_prev()")
m("n", "gi", "lua vim.lsp.buf.implementation()")
m("n", "gr", "lua vim.lsp.buf.references()")
m("n", "K", "lua vim.lsp.buf.hover()")
-- m("n", "<space>rn", "lua vim.lsp.buf.rename()")
m("n", "gl", "lua vim.lsp.diagnostic.show_line_diagnostics()")
-- m("n", "<space>f", "lua vim.lsp.buf.formatting()")
end
-- setup lsp installer
local lsp_installer = require("nvim-lsp-installer")
-- Provide settings first!
lsp_installer.settings({
ui = {
icons = {
server_installed = "✓",
server_pending = "➜",
server_uninstalled = "✗",
},
},
})
lsp_installer.on_server_ready(function(server)
local opts = {
on_attach = on_attach,
capabilities = require("cmp_nvim_lsp").update_capabilities(vim.lsp.protocol.make_client_capabilities()),
flags = {
debounce_text_changes = 150,
},
}
server:setup(opts)
end)
-- lsp settings
require("nlspsettings").setup()
-- diagnostics
vim.diagnostic.config({
virtual_text = false,
underline = true,
float = {
source = "always",
},
severity_sort = true,
--[[ virtual_text = {
prefix = "»",
spacing = 4,
}, ]]
signs = true,
update_in_insert = false,
})
_treesitter.lua
require'nvim-treesitter.configs'.setup {
ensure_installed = "maintained",
sync_install = false,
highlight = {
enable = true,
additional_vim_regex_highlighting = false,
},
}
_telescope.lua
require("telescope").setup({
defaults = {
border = true,
layout_strategy = "bottom_pane",
layout_config = {
height = 0.30,
width = 1.00,
},
-- path_display = { "shorten" },
sorting_strategy = "ascending",
},
})
require("trouble").setup({
icons=false
})
_whichkey.lua
local which_key = {
setup = {
plugins = {
marks = true,
registers = true,
presets = {
operators = false,
motions = false,
text_objects = false,
windows = true,
nav = true,
z = true,
g = true,
},
spelling = { enabled = true, suggestions = 20 },
},
icons = {
breadcrumb = "»",
separator = "➜",
group = "+",
},
window = {
border = "none", -- none, single, double, shadow
position = "bottom", -- bottom, top
margin = { 1, 0, 1, 0 },
padding = { 2, 2, 2, 2 },
},
layout = {
height = { min = 4, max = 25 },
width = { min = 20, max = 50 },
spacing = 3,
},
hidden = { "<silent>", "<cmd>", "<Cmd>", "<CR>", "call", "lua", "^:", "^ " },
show_help = true,
},
opts = {
mode = "n",
prefix = "<leader>",
buffer = nil,
silent = true,
noremap = true,
nowait = true,
},
vopts = {
mode = "v",
prefix = "<leader>",
buffer = nil,
silent = true,
noremap = true,
nowait = true,
},
-- NOTE: Prefer using : over <cmd> as the latter avoids going back in normal-mode.
-- see https://neovim.io/doc/user/map.html#:map-cmd
vmappings = {},
mappings = {
["c"] = { ":BufferClose!<CR>", "Close Buffer" },
["e"] = { ":Telescope file_browser <CR>", "File Browser" },
["f"] = { ":Telescope find_files <CR>", "Find File" },
["h"] = { ":nohlsearch<CR>", "No Highlight" },
b = {
name = "Buffers",
l = { ":Telescope buffers<CR>", "List Buffers" },
b = { ":b#<cr>", "Previous" },
d = { ":bd<cr>", "Delete" },
f = { ":Telescope buffers <cr>", "Find" },
n = { ":bn<cr>", "Next" },
p = { ":bp<cr>", "Previous" },
},
p = {
name = "Packer",
c = { ":PackerCompile<cr>", "Compile" },
i = { ":PackerInstall<cr>", "Install" },
r = { ":lua require('lvim.utils').reload_lv_config()<cr>", "Reload" },
s = { ":PackerSync<cr>", "Sync" },
S = { ":PackerStatus<cr>", "Status" },
u = { ":PackerUpdate<cr>", "Update" },
},
l = {
name = "LSP",
a = { ":Telescope lsp_code_actions<cr>", "Code Action" },
d = {
":Telescope lsp_document_diagnostics<cr>",
"Document Diagnostics",
},
w = {
":Telescope diagnostics<cr>",
"Workspace Diagnostics",
},
f = { ":lua vim.lsp.buf.formatting()<cr>", "Format" },
i = { ":LspInfo<cr>", "Info" },
I = { ":LspInstallInfo<cr>", "Installer Info" },
r = { ":lua vim.lsp.buf.rename()<cr>", "Rename" },
},
s = {
name = "Search",
b = { ":Telescope git_branches <cr>", "Checkout branch" },
c = { ":Telescope colorscheme <cr>", "Colorscheme" },
C = { ":Telescope commands <cr>", "Commands" },
f = { ":Telescope find_files <cr>", "Find File" },
h = { ":Telescope help_tags <cr>", "Find Help" },
j = { ":Telescope jumplist <cr>", "Jumplist" },
k = { ":Telescope keymaps <cr>", "Keymaps" },
M = { ":Telescope man_pages <cr>", "Man Pages" },
r = { ":Telescope oldfiles <cr>", "Open Recent File" },
R = { ":Telescope registers <cr>", "Registers" },
t = { ":Telescope live_grep <cr>", "Text" },
n = { ":Telescope live_grep search_dirs={os.getenv('NOTES')} <cr>", "Notes" },
p = {
":lua require('telescope.builtin.internal').colorscheme({enable_preview = true})<cr>",
"Colorscheme with Preview",
},
},
T = {
name = "Treesitter",
i = { ":TSConfigInfo<cr>", "Info" },
},
t = {
name = "Diagnostics",
t = { "<cmd>TroubleToggle<cr>", "trouble" },
w = { "<cmd>TroubleToggle workspace_diagnostics<cr>", "workspace" },
d = { "<cmd>TroubleToggle document_diagnostics<cr>", "document" },
q = { "<cmd>TroubleToggle quickfix<cr>", "quickfix" },
l = { "<cmd>TroubleToggle loclist<cr>", "loclist" },
r = { "<cmd>TroubleToggle lsp_references<cr>", "references" },
},
},
}
function map(mode, lhs, rhs, opts)
local options = { noremap = true, silent = true }
if opts then
options = vim.tbl_extend("force", options, opts)
end
vim.api.nvim_set_keymap(mode, lhs, rhs, options)
end
map("n", "H", ":bp<CR>")
map("n", "L", ":bn<CR>")
map("n", "<tab>", ":tabnext<CR>")
map("n", "<S-tab>", ":tabprevious<CR>")
map("n", "<C-h>", ":wincmd h<CR>")
map("n", "<C-j>", ":wincmd j<CR>")
map("n", "<C-k>", ":wincmd k<CR>")
map("n", "<C-l>", ":wincmd l<CR>")
local wk = require("which-key")
wk.setup(which_key.setup)
local opts = which_key.opts
local vopts = which_key.vopts
local mappings = which_key.mappings
local vmappings = which_key.vmappings
wk.register(mappings, opts)
wk.register(vmappings, vopts)
if which_key.on_config_done then
which_key.on_config_done(wk)
end
_状态线.lua
local lsp_status = require("lsp-status")
local function lsp_progress()
return lsp_status.status()
end
local function extract_highlight_colors(color_group, scope)
if vim.fn.hlexists(color_group) == 0 then
return nil
end
local color = vim.api.nvim_get_hl_by_name(color_group, true)
if color.background ~= nil then
color.bg = string.format("#%06x", color.background)
color.background = nil
end
if color.foreground ~= nil then
color.fg = string.format("#%06x", color.foreground)
color.foreground = nil
end
if scope then
return color[scope]
end
return color
end
local colors = {
gray = 8,
red = 9,
green = 10,
yellow = 11,
blue = 12,
magenta = 13,
cyan = 14,
white = 15,
background = extract_highlight_colors("StatusLine", "bg"),
foreground = 7,
}
local custom_theme = {
normal = {
a = { bg = colors.foreground, fg = colors.background },
b = { bg = colors.background, fg = colors.foreground },
c = { bg = colors.background, fg = colors.foreground },
},
insert = {
a = { bg = colors.cyan, fg = colors.background },
b = { bg = colors.background, fg = colors.cyan },
c = { bg = colors.background, fg = colors.foreground },
},
visual = {
a = { bg = colors.yellow, fg = colors.background },
b = { bg = colors.background, fg = colors.yellow },
c = { bg = colors.background, fg = colors.foreground },
},
replace = {
a = { bg = colors.red, fg = colors.background },
b = { bg = colors.background, fg = colors.red },
c = { bg = colors.background, fg = colors.foreground },
},
command = {
a = { bg = colors.magenta, fg = colors.background },
b = { bg = colors.background, fg = colors.magenta },
c = { bg = colors.background, fg = colors.foreground },
},
inactive = {
a = { bg = colors.background, fg = colors.gray },
b = { bg = colors.background, fg = colors.gray },
c = { bg = colors.background, fg = colors.gray },
},
}
local components = {
mode = {
function()
return " "
end,
padding = { left = 0, right = 0 },
},
filename = {
"filename",
},
diagnostics = {
"diagnostics",
sources = { "nvim_diagnostic" },
symbols = { error = " ", warn = " ", info = " ", hint = " " },
},
treesitter = {
function()
local b = vim.api.nvim_get_current_buf()
if next(vim.treesitter.highlighter.active[b]) then
return ""
end
return ""
end,
},
location = { "location" },
progress = { "progress" },
encoding = {
"o:encoding",
fmt = string.upper,
},
filetype = { "filetype" },
}
require("lualine").setup({
options = {
theme = custom_theme,
icons_enabled = false,
component_separators = { left = "", right = "" },
section_separators = { left = "", right = "" },
disabled_filetypes = { "dashboard", "NvimTree", "Outline" },
},
sections = {
lualine_a = {},
lualine_b = {
components.filename,
},
lualine_c = {
components.diff,
},
lualine_x = {
lsp_progress,
components.diagnostics,
},
lualine_y = {
components.treesitter,
},
lualine_z = {},
},
inactive_sections = {
lualine_a = {},
lualine_b = {
"filename",
},
lualine_c = {},
lualine_x = {},
lualine_y = {},
lualine_z = {},
},
tabline = {
lualine_a = {},
lualine_b = { { "buffers" } },
lualine_c = {},
lualine_x = {},
lualine_y = { { "tabs", mode = 0 } },
lualine_z = {},
},
extensions = { "nvim-tree" },
})
结论
我知道配置太多了!对此我深感抱歉。但我认为有了这么多配置,任何人都可以自定义所有内容。我做的设置仅供参考,我对 Vim 应该如何设计有自己的看法。希望这只是一个基础,大家可以以此为基础,开始使用,然后根据自己的喜好进行修改。我还留下了一些注释掉的设置,以便进一步修改。
如果你已经做到这里,下一步就是尝试一下。看看它是如何工作的。
main.py 测试
创建一个文件nvim main.py
。首次加载时nvim
会报错,因为没有安装任何插件。继续输入q
直到报错结束。我们需要重新启动已经安装好的插件。nvim
然后。现在我们有了打包程序,我们可以安装和更新定义在的所有插件。再关闭一次。现在我们完成了这些,然后开始测试我们的设置。(注意正在安装一些东西)让它在后台发生。等待完成后再退出。将此代码片段放入文件中。packer
qa
nvim main.py
:PackerSync
_plugins.lua
nvim
:qa
nvim main.py
tree-sitter
tree-sitter
#!/usr/bin/env python
def echo(msg):
print msg
if __name__ == "__main__":
echo("Hello World!")
节省:w
安装 pyright 服务器
现在我们需要安装 lsp 服务器来处理 Python 文件。这可以通过几种不同的方式完成,最快的是:LspInstall pyright
,或者也可以使用 访问“仪表板” :LspInstallInfo
,然后使用帮助?
查看选项。u
将升级当前光标下的服务器,并将i
安装当前光标下的服务器。
现在pyright
已安装完毕,让我们确保它能正常工作。
注意,第 4 行msg
带有下划线,并且E
在 gitgutter 中显示为 。将光标移动到第 4 行并触发 行诊断gl
。如果看到弹出窗口,则说明配置正确!
黑色格式化程序
我没有详细介绍安装的语言服务器的自定义设置,以及格式化和 linting 的设置。这里
使用 wblack
作为 Python 格式化程序。我们在这里使用null-ls进行格式化。
after/ftplugin/python.lua
local null_ls = require("null-ls")
local sources = {
null_ls.builtins.formatting.black,
}
null_ls.setup({ sources = sources })
注意, null_ls 可以像我们创建的其他 Lua 文件一样,以常规方式设置所有设置。使用 after dir / 延迟加载只是其中一种方法。
确保已安装 black pip install black
。让我们用我们的文件试试看main.py
。将其更新为以下内容,然后保存:w
。
#!/usr/bin/env python
def echo(msg):
print(msg)
if __name__ == "__main__":
echo("Hello World!")
- 注意
leader
space
现在告诉nvim
使用定义的格式化程序发送格式化leader lf
。
结果应该是一个格式良好的python
文件。
这就是全部内容了。
文章来源:https://dev.to/casonadams/neovim-lsp-to-replace-vscode-n8c