视频与目录
项目 | 值 |
---|
教程目录 | https://blog.csdn.net/lxyoucan/article/details/120641546 |
视频全屏 | https://www.bilibili.com/video/BV19T4y1Z7VB/ |
视频 |
06Native LSP 自动补全、语法检查、code action、代码段—TypeScript篇
|
06_[nvim0.5+从0单排]_Native LSP 自动补全、语法检查、code action、代码段—TypeScript篇
本文地址:
https://blog.csdn.net/lxyoucan/article/details/120632264
大家好,我是ITKEY欢迎来到nvim0.5+从0单排系列第6期。
本期以TypeScript为例,把nvim打造成一个功能强大的IDE。
版本
nvim 版本必须在 0.5.0以上,低版本是不支持Native LSP的。低版本想实现类似的效果可以使用neoclide/coc.nvim
插件。
核心插件安装
use "neovim/nvim-lspconfig"
use {
"hrsh7th/nvim-cmp",
requires = {
"hrsh7th/cmp-nvim-lsp",
"onsails/lspkind-nvim",
"hrsh7th/cmp-buffer",
"hrsh7th/cmp-nvim-lua",
"octaltree/cmp-look",
"hrsh7th/cmp-path",
"hrsh7th/cmp-calc",
"f3fora/cmp-spell",
"hrsh7th/cmp-emoji",
}
}
use {
"L3MON4D3/LuaSnip",
requires = {
"saadparwaiz1/cmp_luasnip",
"rafamadriz/friendly-snippets"
}
}
typescript-language-server安装
首先需要安装node.js,安装方法可以参考:https://blog.csdn.net/lxyoucan/article/details/120466390
npm安装方式:
npm i -g typescript typescript-language-server
yarn安装方式:
yarn global add typescript typescript-language-server
lspconfig 配置
新增配置文件
~/.config/nvim/after/plugin/lspconfig.rc.vim
文件内容如下:
if !exists('g:lspconfig')
finish
endif
lua << EOF
--提示信息自定义图标
-- icon
vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, {
underline = true,
-- This sets the spacing and the prefix, obviously.
virtual_text = {
spacing = 4,
prefix = ''
}
}
)
EOF
nvim-cmp配置
自动补全类的插件有很多选择,我为什么选择了nvim-cmp呢?主要还是因为lspconfig的wiki中是以这个插件为例说明的。用起来还行,所以就用它了。
typescript相关配置
新增配置文件
~/.config/nvim/lua/lspconf/typescript.lua
文件内容如下:
local nvim_lsp = require('lspconfig')
Itkey_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')
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', 'gi', '<cmd>lua vim.lsp.buf.implementation()<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', '<S-C-j>', '<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", "<leader>f", "<cmd>lua vim.lsp.buf.formatting()<CR>", opts)
if client.resolved_capabilities.document_formatting then
vim.api.nvim_command [[augroup Format]]
vim.api.nvim_command [[autocmd! * <buffer>]]
vim.api.nvim_command [[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_seq_sync()]]
vim.api.nvim_command [[augroup END]]
end
end
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.textDocument.completion.completionItem.documentationFormat = { 'markdown', 'plaintext' }
capabilities.textDocument.completion.completionItem.snippetSupport = true
capabilities.textDocument.completion.completionItem.preselectSupport = true
capabilities.textDocument.completion.completionItem.insertReplaceSupport = true
capabilities.textDocument.completion.completionItem.labelDetailsSupport = true
capabilities.textDocument.completion.completionItem.deprecatedSupport = true
capabilities.textDocument.completion.completionItem.commitCharactersSupport = true
capabilities.textDocument.completion.completionItem.tagSupport = { valueSet = { 1 } }
capabilities.textDocument.completion.completionItem.resolveSupport = {
properties = {
'documentation',
'detail',
'additionalTextEdits',
},
}
Itkey_capabilities = capabilities
local servers = {'tsserver'}
for _, lsp in ipairs(servers) do
nvim_lsp[lsp].setup {
on_attach = Itkey_on_attach,
capabilities = Itkey_capabilities,
}
end
nvim-cmp配置
2021年10月12日配置调整说明,以下内容与视频中稍有出入,但是影响不大。主要让配置兼容新版的nvim-cmp
。
新增配置文件
~/.config/nvim/after/plugin/nvim-cmp.lua
文件内容如下:
local status, nvim_lsp = pcall(require, "lspconfig")
if (not status) then
return
end
require("lspconf.typescript")
vim.o.completeopt = "menuone,noselect"
local luasnip = require "luasnip"
local lspkind = require("lspkind")
local cmp = require "cmp"
local cmpFormat1 = function(entry, vim_item)
vim_item.kind = require("lspkind").presets.default[vim_item.kind] .. " " .. vim_item.kind
vim_item.menu =
({
buffer = "[Buffer]",
nvim_lsp = "[LSP]",
ultisnips = "[UltiSnips]",
nvim_lua = "[Lua]",
cmp_tabnine = "[TabNine]",
look = "[Look]",
path = "[Path]",
spell = "[Spell]",
calc = "[Calc]",
emoji = "[Emoji]"
})[entry.source.name]
return vim_item
end
local cmpFormat2 = function(entry, vim_item)
vim_item.kind = lspkind.presets.default[vim_item.kind]
return vim_item
end
local cmpFormat3 = function(entry, vim_item)
vim_item.kind = require("lspkind").presets.default[vim_item.kind] .. ""
vim_item.menu =
({
buffer = "[Buffer]",
nvim_lsp = "",
ultisnips = "[UltiSnips]",
nvim_lua = "[Lua]",
cmp_tabnine = "[TabNine]",
look = "[Look]",
path = "[Path]",
spell = "[Spell]",
calc = "[Calc]",
emoji = "[Emoji]"
})[entry.source.name]
return vim_item
end
local keymap = require("cmp.utils.keymap")
cmp.confirm = function(option)
option = option or {}
local e = cmp.core.view:get_selected_entry() or (option.select and cmp.core.view:get_first_entry() or nil)
if e then
cmp.core:confirm(
e,
{
behavior = option.behavior
},
function()
local myContext = cmp.core:get_context({reason = cmp.ContextReason.TriggerOnly})
cmp.core:complete(myContext)
if
e and e.resolved_completion_item and
(e.resolved_completion_item.kind == 3 or e.resolved_completion_item.kind == 2)
then
vim.api.nvim_feedkeys(keymap.t("()<Left>"), "n", true)
end
end
)
return true
else
if vim.fn.complete_info({"selected"}).selected ~= -1 then
keymap.feedkeys(keymap.t("<C-y>"), "n")
return true
end
return false
end
end
cmp.setup {
formatting = {
format = cmpFormat1
},
snippet = {
expand = function(args)
require("luasnip").lsp_expand(args.body)
end
},
mapping = {
["<C-p>"] = cmp.mapping.select_prev_item(),
["<C-n>"] = cmp.mapping.select_next_item(),
["<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 {
behavior = cmp.ConfirmBehavior.Replace,
select = false
},
['<Tab>'] = function(fallback)
if cmp.visible() then
cmp.select_next_item()
elseif luasnip.expand_or_jumpable() then
vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<Plug>luasnip-expand-or-jump', true, true, true), '')
else
fallback()
end
end,
['<S-Tab>'] = function(fallback)
if cmp.visible() then
cmp.select_prev_item()
elseif luasnip.jumpable(-1) then
vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<Plug>luasnip-jump-prev', true, true, true), '')
else
fallback()
end
end
},
sources = {
{name = "nvim_lsp"},
{name = "luasnip"},
{
name = "buffer",
option = {
get_bufnrs = function()
return vim.api.nvim_list_bufs()
end
}
},
{name = "path"}
}
}
L3MON4D3/LuaSnip配置
新增配置文件
~/.config/nvim/after/plugin/snippets.lua
文件内容如下:
local ls = require("luasnip")
local s = ls.snippet
local sn = ls.snippet_node
local t = ls.text_node
local i = ls.insert_node
local f = ls.function_node
local c = ls.choice_node
local d = ls.dynamic_node
local l = require("luasnip.extras").lambda
local r = require("luasnip.extras").rep
local p = require("luasnip.extras").partial
local m = require("luasnip.extras").match
local n = require("luasnip.extras").nonempty
local dl = require("luasnip.extras").dynamic_lambda
local fmt = require("luasnip.extras.fmt").fmt
local fmta = require("luasnip.extras.fmt").fmta
local types = require("luasnip.util.types")
local conds = require("luasnip.extras.expand_conditions")
ls.config.set_config({
history = true,
updateevents = "TextChanged,TextChangedI",
ext_opts = {
[types.choiceNode] = {
active = {
virt_text = { { "choiceNode", "Comment" } },
},
},
},
ext_base_prio = 300,
ext_prio_increase = 1,
enable_autosnippets = true,
})
local function copy(args)
return args[1]
end
local rec_ls
rec_ls = function()
return sn(
nil,
c(1, {
t(""),
sn(nil, { t({ "", "\t\\item " }), i(1), d(2, rec_ls, {}) }),
})
)
end
local function jdocsnip(args, _, old_state)
local nodes = {
t({ "/**", " * " }),
i(1, "A short Description"),
t({ "", "" }),
}
local param_nodes = {}
if old_state then
nodes[2] = i(1, old_state.descr:get_text())
end
param_nodes.descr = nodes[2]
if string.find(args[2][1], ", ") then
vim.list_extend(nodes, { t({ " * ", "" }) })
end
local insert = 2
for indx, arg in ipairs(vim.split(args[2][1], ", ", true)) do
arg = vim.split(arg, " ", true)[2]
if arg then
local inode
if old_state and old_state[arg] then
inode = i(insert, old_state["arg" .. arg]:get_text())
else
inode = i(insert)
end
vim.list_extend(
nodes,
{ t({ " * @param " .. arg .. " " }), inode, t({ "", "" }) }
)
param_nodes["arg" .. arg] = inode
insert = insert + 1
end
end
if args[1][1] ~= "void" then
local inode
if old_state and old_state.ret then
inode = i(insert, old_state.ret:get_text())
else
inode = i(insert)
end
vim.list_extend(
nodes,
{ t({ " * ", " * @return " }), inode, t({ "", "" }) }
)
param_nodes.ret = inode
insert = insert + 1
end
if vim.tbl_count(args[3]) ~= 1 then
local exc = string.gsub(args[3][2], " throws ", "")
local ins
if old_state and old_state.ex then
ins = i(insert, old_state.ex:get_text())
else
ins = i(insert)
end
vim.list_extend(
nodes,
{ t({ " * ", " * @throws " .. exc .. " " }), ins, t({ "", "" }) }
)
param_nodes.ex = ins
insert = insert + 1
end
vim.list_extend(nodes, { t({ " */" }) })
local snip = sn(nil, nodes)
snip.old_state = param_nodes
return snip
end
local function bash(_, _, command)
local file = io.popen(command, "r")
local res = {}
for line in file:lines() do
table.insert(res, line)
end
return res
end
local date_input = function(args, state, fmt)
local fmt = fmt or "%Y-%m-%d"
return sn(nil, i(1, os.date(fmt)))
end
ls.snippets = {
all = {
s("fn", {
t("//Parameters: "),
f(copy, 2),
t({ "", "function " }),
i(1),
t("("),
i(2, "int foo"),
t({ ") {", "\t" }),
i(0),
t({ "", "}" }),
}),
s("class", {
c(1, {
t("public "),
t("private "),
}),
t("class "),
i(2),
t(" "),
c(3, {
t("{"),
sn(nil, {
t("extends "),
i(1),
t(" {"),
}),
sn(nil, {
t("implements "),
i(1),
t(" {"),
}),
}),
t({ "", "\t" }),
i(0),
t({ "", "}" }),
}),
s("novel", {
t("It was a dark and stormy night on "),
d(1, date_input, {}, "%A, %B %d of %Y"),
t(" and the clocks were striking thirteen."),
}),
ls.parser.parse_snippet(
"lspsyn",
"Wow! This ${1:Stuff} really ${2:works. ${3:Well, a bit.}}"
),
ls.parser.parse_snippet(
{ trig = "te", wordTrig = false },
"${1:cond} ? ${2:true} : ${3:false}"
),
ls.parser.parse_snippet({ trig = "%d", regTrig = true }, "A Number!!"),
s("cond", {
t("will only expand in c-style comments"),
}, {
condition = function(line_to_cursor, matched_trigger, captures)
return line_to_cursor:match("%s*//")
end,
}),
s("cond2", {
t("will only expand at the beginning of the line"),
}, {
condition = conds.line_begin,
}),
s(
{ trig = "a%d", regTrig = true },
f(function(_, snip)
return "Triggered with " .. snip.trigger .. "."
end, {})
),
s(
{ trig = "b(%d)", regTrig = true },
f(function(_, snip)
return "Captured Text: " .. snip.captures[1] .. "."
end, {})
),
s({ trig = "c(%d+)", regTrig = true }, {
t("will only expand for even numbers"),
}, {
condition = function(line_to_cursor, matched_trigger, captures)
return tonumber(captures[1]) % 2 == 0
end,
}),
s("bash", f(bash, {}, "ls")),
s("transform", {
i(1, "initial text"),
t({ "", "" }),
l(l._1:match("[^i]*$"):gsub("i", "o"):gsub(" ", "_"):upper(), 1),
}),
s("transform2", {
i(1, "initial text"),
t("::"),
i(2, "replacement for e"),
t({ "", "" }),
l(l._1:gsub("e", l._2), { 1, 2 }),
}),
s({ trig = "trafo(%d+)", regTrig = true }, {
l(l.CAPTURE1:gsub("1", l.TM_FILENAME), {}),
}),
s("link_url", {
t('<a href="'),
f(function(_, snip)
return snip.env.TM_SELECTED_TEXT[1] or {}
end, {}),
t('">'),
i(1),
t("</a>"),
i(0),
}),
s("repeat", { i(1, "text"), t({ "", "" }), r(1) }),
s("part", p(os.date, "%Y")),
s("mat", {
i(1, { "sample_text" }),
t(": "),
m(1, "%d", "contains a number", "no number :("),
}),
s("mat2", {
i(1, { "sample_text" }),
t(": "),
m(1, "[abc][abc][abc]"),
}),
s("mat3", {
i(1, { "sample_text" }),
t(": "),
m(
1,
l._1:gsub("[123]", ""):match("%d"),
"contains a number that isn't 1, 2 or 3!"
),
}),
s("mat4", {
i(1, { "sample_text" }),
t(": "),
m(1, function(text)
return (#text % 2 == 0 and text) or nil
end),
}),
s("nempty", {
i(1, "sample_text"),
n(1, "i(1) is not empty!"),
}),
s("dl1", {
i(1, "sample_text"),
t({ ":", "" }),
dl(2, l._1, 1),
}),
s("dl2", {
i(1, "sample_text"),
i(2, "sample_text_2"),
t({ "", "" }),
dl(3, l._1:gsub("\n", " linebreak ") .. l._2, { 1, 2 }),
}),
s(
"fmt1",
fmt("To {title} {} {}.", {
i(2, "Name"),
i(3, "Surname"),
title = c(1, { t("Mr."), t("Ms.") }),
})
),
s(
"fmt2",
fmt(
[[
foo({1}, {3}) {{
return {2} * {4}
}}
]],
{
i(1, "x"),
r(1),
i(2, "y"),
r(2),
}
)
),
s(
"fmt3",
fmt("{} {a} {} {1} {}", {
t("1"),
t("2"),
a = t("A"),
})
),
s("fmt4", fmt("foo() { return []; }", i(1, "x"), { delimiters = "[]" })),
s("fmt5", fmta("foo() { return <>; }", i(1, "x"))),
s(
"fmt6",
fmt("use {} only", { t("this"), t("not this") }, { strict = false })
),
},
java = {
s("fn", {
d(6, jdocsnip, { 2, 4, 5 }),
t({ "", "" }),
c(1, {
t("public "),
t("private "),
}),
c(2, {
t("void"),
t("String"),
t("char"),
t("int"),
t("double"),
t("boolean"),
i(nil, ""),
}),
t(" "),
i(3, "myFunc"),
t("("),
i(4),
t(")"),
c(5, {
t(""),
sn(nil, {
t({ "", " throws " }),
i(1),
}),
}),
t({ " {", "\t" }),
i(0),
t({ "", "}" }),
}),
},
tex = {
s("ls", {
t({ "\\begin{itemize}", "\t\\item " }),
i(1),
d(2, rec_ls, {}),
t({ "", "\\end{itemize}" }),
}),
},
}
ls.autosnippets = {
all = {
s("autotrigger", {
t("autosnippet"),
}),
},
}
ls.filetype_extend("lua", { "c" })
ls.filetype_set("cpp", { "c" })
require("luasnip/loaders/from_vscode").load()
更新说明
2021年11月1日更新
由于相关插件更新,导致配置文件微调
- 微调
~/.config/nvim/after/plugin/snippets.lua
- 微调
~/.config/nvim/after/plugin/nvim-cmp.lua
相关地址
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)