├── nvim ├── init.lua ├── lua │ └── plugins.lua ├── after │ └── plugin │ │ ├── nvim-cmp.lua │ │ └── snippets.lua └── ftplugin │ └── java.lua └── README.md /nvim/init.lua: -------------------------------------------------------------------------------- 1 | --插件管理器 2 | require("plugins") 3 | --主题设置 4 | vim.cmd("colorscheme " .. "gruvbox-material") 5 | ------按键映射 start------ 6 | local opts = {noremap = true, silent = true} 7 | local keymap = vim.api.nvim_set_keymap 8 | --把空格键设置成 9 | vim.g.mapleader = " " 10 | --快速跳转行首与行尾 11 | keymap('n', 'L', '$', opts) 12 | keymap('v', 'L', '$', opts) 13 | keymap('n', 'H', '^', opts) 14 | keymap('v', 'H', '^', opts) 15 | --插入模式jk当Esc 16 | keymap('i', 'jk', '', opts) 17 | --保 存 18 | keymap('n', '', ':w', opts) 19 | keymap('i', '', ' :w', opts) 20 | --全选 21 | keymap('n', '', 'ggG', opts) 22 | ------按键映射 end ------ 23 | -- 文件编码格式 24 | vim.opt.fileencoding = "utf-8" 25 | -- 显示行号 26 | vim.opt.number=true 27 | -- tab=5个空格 28 | vim.opt.tabstop=5 29 | vim.opt.shiftwidth=5 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /nvim/lua/plugins.lua: -------------------------------------------------------------------------------- 1 | ---@diagnostic disable: undefined-global 2 | --在没有安装packer的电脑上,自动安装packer插件 3 | local fn = vim.fn 4 | local install_path = fn.stdpath("data") .. "/site/pack/packer/start/packer.nvim" 5 | if fn.empty(fn.glob(install_path)) > 0 then 6 | fn.system( 7 | {"git", "clone", "--depth", "1", "https://gitcode.net/mirrors/wbthomason/packer.nvim", install_path} 8 | ) --csdn加速镜像 9 | vim.cmd "packadd packer.nvim" 10 | end 11 | -- Only required if you have packer configured as `opt` 12 | --【国内加速】插件名称超长的说明: 13 | --由于国内网络环境访问github及其不稳定,所以如果在gitcode.net上的镜像的(https://gitcode.net/mirrors/开头的),我们尽量使用。这样可以提高访问速度。 14 | --gitcode.net没有镜像的部分(https://gitcode.net/lxyoucan开头的),是我手动clone到gitcode上的不定期更新。 15 | --如果你访问github比较流畅,插件名称只保留后两段即如:neovim/nvim-lspconfig 16 | vim.cmd [[packadd packer.nvim]] 17 | return require("packer").startup(function() 18 | -- Packer可以管理自己的更新 19 | use "https://gitcode.net/mirrors/wbthomason/packer.nvim" 20 | --Nvim LSP 客户端的快速入门配置 21 | use "https://gitcode.net/mirrors/neovim/nvim-lspconfig" 22 | --自动提示插件 23 | use { 24 | "https://gitcode.net/mirrors/hrsh7th/nvim-cmp", 25 | requires = { 26 | "https://gitcode.net/lxyoucan/cmp-nvim-lsp", --neovim 内置 LSP 客户端的 nvim-cmp 源 27 | "https://gitcode.net/lxyoucan/cmp-buffer", --从buffer中智能提示 28 | "https://gitcode.net/lxyoucan/cmp-path" --自动提示硬盘上的文件 29 | } 30 | } 31 | -- java语言支持 32 | use "https://gitcode.net/lxyoucan/nvim-jdtls.git" 33 | -- 代码段提示 34 | use { 35 | "https://gitcode.net/mirrors/L3MON4D3/LuaSnip", 36 | requires = { 37 | "https://gitcode.net/lxyoucan/cmp_luasnip", -- Snippets source for nvim-cmp 38 | "https://gitcode.net/lxyoucan/friendly-snippets" --代码段合集 39 | } 40 | } 41 | --主题安装 42 | use "https://gitcode.net/mirrors/sainnhe/gruvbox-material" 43 | end) 44 | 45 | 46 | -------------------------------------------------------------------------------- /nvim/after/plugin/nvim-cmp.lua: -------------------------------------------------------------------------------- 1 | local status, nvim_lsp = pcall(require, "lspconfig") 2 | if (not status) then 3 | return 4 | end 5 | 6 | -- Set completeopt to have a better completion experience 7 | vim.o.completeopt = "menuone,noselect" 8 | 9 | -- luasnip setup 10 | local luasnip = require "luasnip" 11 | 12 | -- nvim-cmp setup 13 | local cmp = require "cmp" 14 | cmp.setup { 15 | snippet = { 16 | expand = function(args) 17 | require("luasnip").lsp_expand(args.body) 18 | end 19 | }, 20 | mapping = { 21 | [""] = cmp.mapping.select_prev_item(), 22 | [""] = cmp.mapping.select_next_item(), 23 | [""] = cmp.mapping.scroll_docs(-4), 24 | [""] = cmp.mapping.scroll_docs(4), 25 | [""] = cmp.mapping.complete(), 26 | [""] = cmp.mapping.close(), 27 | [""] = cmp.mapping.confirm { 28 | behavior = cmp.ConfirmBehavior.Replace, 29 | select = false 30 | }, 31 | [""] = function(fallback) 32 | if cmp.visible() then 33 | cmp.select_next_item() 34 | elseif luasnip.expand_or_jumpable() then 35 | vim.fn.feedkeys(vim.api.nvim_replace_termcodes("luasnip-expand-or-jump", true, true, true), "") 36 | else 37 | fallback() 38 | end 39 | end, 40 | [""] = function(fallback) 41 | if cmp.visible() then 42 | cmp.select_prev_item() 43 | elseif luasnip.jumpable(-1) then 44 | vim.fn.feedkeys(vim.api.nvim_replace_termcodes("luasnip-jump-prev", true, true, true), "") 45 | else 46 | fallback() 47 | end 48 | end 49 | }, 50 | sources = { 51 | {name = "nvim_lsp"}, 52 | {name = "luasnip"}, 53 | { 54 | name = "buffer", 55 | option = { 56 | get_bufnrs = function() 57 | return vim.api.nvim_list_bufs() 58 | end 59 | } 60 | }, 61 | {name = "path"} 62 | } 63 | } 64 | 65 | 66 | -------------------------------------------------------------------------------- /nvim/ftplugin/java.lua: -------------------------------------------------------------------------------- 1 | local config = { 2 | cmd = { 3 | "java", 4 | "-Declipse.application=org.eclipse.jdt.ls.core.id1", 5 | "-Dosgi.bundles.defaultStartLevel=4", 6 | "-Declipse.product=org.eclipse.jdt.ls.core.product", 7 | "-Dlog.protocol=true", 8 | "-Dlog.level=ALL", 9 | "-Xms1g", 10 | "--add-modules=ALL-SYSTEM", 11 | "--add-opens", 12 | "java.base/java.util=ALL-UNNAMED", 13 | "--add-opens", 14 | "java.base/java.lang=ALL-UNNAMED", 15 | --增加lombok插件支持,getter setter good bye 16 | "-javaagent:/home/vnc/.local/share/nvim/lsp/jdt-language-server/lombok.jar", 17 | "-Xbootclasspath/a:/home/vnc/.local/share/nvim/lsp/jdt-language-server/lombok.jar", 18 | "-jar", 19 | "/home/vnc/.local/share/nvim/lsp/jdt-language-server/plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar", 20 | "-configuration", 21 | "/home/vnc/.local/share/nvim/lsp/jdt-language-server/config_linux", 22 | "-data", 23 | "/home/vnc/.local/share/nvim/lsp/jdt-language-server/workspace/folder" 24 | }, 25 | root_dir = require("jdtls.setup").find_root({".git", "mvnw", "gradlew"}), 26 | settings = { 27 | java = {} 28 | }, 29 | init_options = { 30 | bundles = {} 31 | } 32 | } 33 | require("jdtls").start_or_attach(config) 34 | 35 | local current_buff = vim.api.nvim_get_current_buf 36 | -- 在语言服务器附加到当前缓冲区之后 37 | -- 使用 on_attach 函数仅映射以下键 38 | local java_on_attach = function(client, bufnr) 39 | local function buf_set_keymap(...) 40 | vim.api.nvim_buf_set_keymap(bufnr, ...) 41 | end 42 | local function buf_set_option(...) 43 | vim.api.nvim_buf_set_option(bufnr, ...) 44 | end 45 | 46 | --Enable completion triggered by 47 | buf_set_option("omnifunc", "v:lua.vim.lsp.omnifunc") 48 | -- Mappings. 49 | local opts = {noremap = true, silent = true} 50 | -- See `:help vim.lsp.*` for documentation on any of the below functions 51 | buf_set_keymap("n", "gD", "lua vim.lsp.buf.declaration()", opts) 52 | buf_set_keymap("n", "gd", "lua vim.lsp.buf.definition()", opts) 53 | --buf_set_keymap('n', 'K', 'lua vim.lsp.buf.hover()', opts) 54 | buf_set_keymap("n", "gi", "lua vim.lsp.buf.implementation()", opts) 55 | --buf_set_keymap('i', '', 'lua vim.lsp.buf.signature_help()', opts) 56 | buf_set_keymap("n", "wa", "lua vim.lsp.buf.add_workspace_folder()", opts) 57 | buf_set_keymap("n", "wr", "lua vim.lsp.buf.remove_workspace_folder()", opts) 58 | buf_set_keymap("n", "wl", "lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))", opts) 59 | buf_set_keymap("n", "D", "lua vim.lsp.buf.type_definition()", opts) 60 | --重命名 61 | buf_set_keymap("n", "rn", "lua vim.lsp.buf.rename()", opts) 62 | --智能提醒,比如:自动导包 已经用lspsaga里的功能替换了 63 | buf_set_keymap("n", "ca", "lua vim.lsp.buf.code_action()", opts) 64 | buf_set_keymap("n", "gr", "lua vim.lsp.buf.references()", opts) 65 | buf_set_keymap("n", "e", "lua vim.lsp.diagnostic.show_line_diagnostics()", opts) 66 | --buf_set_keymap('n', '', 'lua vim.lsp.diagnostic.goto_prev()', opts) 67 | buf_set_keymap("n", "", "lua vim.lsp.diagnostic.goto_next()", opts) 68 | buf_set_keymap("n", "q", "lua vim.lsp.diagnostic.set_loclist()", opts) 69 | --代码格式化 70 | buf_set_keymap("n", "f", "lua vim.lsp.buf.formatting()", opts) 71 | buf_set_keymap("n", "l", "lua vim.lsp.buf.formatting()", opts) 72 | buf_set_keymap("n", "l", "lua vim.lsp.buf.formatting()", opts) 73 | --自动导入全部缺失的包,自动删除多余的未用到的包 74 | buf_set_keymap("n", "", "lua require'jdtls'.organize_imports()", opts) 75 | --引入局部变量的函数 function to introduce a local variable 76 | buf_set_keymap("n", "crv", "lua require('jdtls').extract_variable()", opts) 77 | buf_set_keymap("v", "crv", "lua require('jdtls').extract_variable(true)", opts) 78 | --function to extract a constant 79 | buf_set_keymap("n", "crc", "lua require('jdtls').extract_constant()", opts) 80 | buf_set_keymap("v", "crc", "lua require('jdtls').extract_constant(true)", opts) 81 | --将一段代码提取成一个额外的函数function to extract a block of code into a method 82 | buf_set_keymap("v", "crm", "lua require('jdtls').extract_method(true)", opts) 83 | 84 | -- 代码保存自动格式化formatting 85 | vim.api.nvim_command [[autocmd BufWritePre lua vim.lsp.buf.formatting_seq_sync()]] 86 | end 87 | 88 | java_on_attach(nil, current_buff) 89 | 90 | 91 | -------------------------------------------------------------------------------- /nvim/after/plugin/snippets.lua: -------------------------------------------------------------------------------- 1 | local ls = require("luasnip") 2 | -- some shorthands... 3 | local s = ls.snippet 4 | local sn = ls.snippet_node 5 | local t = ls.text_node 6 | local i = ls.insert_node 7 | local f = ls.function_node 8 | local c = ls.choice_node 9 | local d = ls.dynamic_node 10 | local r = ls.restore_node 11 | local l = require("luasnip.extras").lambda 12 | local rep = require("luasnip.extras").rep 13 | local p = require("luasnip.extras").partial 14 | local m = require("luasnip.extras").match 15 | local n = require("luasnip.extras").nonempty 16 | local dl = require("luasnip.extras").dynamic_lambda 17 | local fmt = require("luasnip.extras.fmt").fmt 18 | local fmta = require("luasnip.extras.fmt").fmta 19 | local types = require("luasnip.util.types") 20 | local conds = require("luasnip.extras.expand_conditions") 21 | 22 | -- If you're reading this file for the first time, best skip to around line 190 23 | -- where the actual snippet-definitions start. 24 | 25 | -- Every unspecified option will be set to the default. 26 | ls.config.set_config({ 27 | history = true, 28 | -- Update more often, :h events for more info. 29 | updateevents = "TextChanged,TextChangedI", 30 | -- Snippets aren't automatically removed if their text is deleted. 31 | -- `delete_check_events` determines on which events (:h events) a check for 32 | -- deleted snippets is performed. 33 | -- This can be especially useful when `history` is enabled. 34 | delete_check_events = "TextChanged", 35 | ext_opts = { 36 | [types.choiceNode] = { 37 | active = { 38 | virt_text = { { "choiceNode", "Comment" } }, 39 | }, 40 | }, 41 | }, 42 | -- treesitter-hl has 100, use something higher (default is 200). 43 | ext_base_prio = 300, 44 | -- minimal increase in priority. 45 | ext_prio_increase = 1, 46 | enable_autosnippets = true, 47 | -- mapping for cutting selected text so it's usable as SELECT_DEDENT, 48 | -- SELECT_RAW or TM_SELECTED_TEXT (mapped via xmap). 49 | store_selection_keys = "", 50 | -- luasnip uses this function to get the currently active filetype. This 51 | -- is the (rather uninteresting) default, but it's possible to use 52 | -- eg. treesitter for getting the current filetype by setting ft_func to 53 | -- require("luasnip.extras.filetype_functions").from_cursor (requires 54 | -- `nvim-treesitter/nvim-treesitter`). This allows correctly resolving 55 | -- the current filetype in eg. a markdown-code block or `vim.cmd()`. 56 | ft_func = function() 57 | return vim.split(vim.bo.filetype, ".", true) 58 | end, 59 | }) 60 | 61 | -- args is a table, where 1 is the text in Placeholder 1, 2 the text in 62 | -- placeholder 2,... 63 | local function copy(args) 64 | return args[1] 65 | end 66 | 67 | -- 'recursive' dynamic snippet. Expands to some text followed by itself. 68 | local rec_ls 69 | rec_ls = function() 70 | return sn( 71 | nil, 72 | c(1, { 73 | -- Order is important, sn(...) first would cause infinite loop of expansion. 74 | t(""), 75 | sn(nil, { t({ "", "\t\\item " }), i(1), d(2, rec_ls, {}) }), 76 | }) 77 | ) 78 | end 79 | 80 | -- complicated function for dynamicNode. 81 | local function jdocsnip(args, _, old_state) 82 | -- !!! old_state is used to preserve user-input here. DON'T DO IT THAT WAY! 83 | -- Using a restoreNode instead is much easier. 84 | -- View this only as an example on how old_state functions. 85 | local nodes = { 86 | t({ "/**", " * " }), 87 | i(1, "A short Description"), 88 | t({ "", "" }), 89 | } 90 | 91 | -- These will be merged with the snippet; that way, should the snippet be updated, 92 | -- some user input eg. text can be referred to in the new snippet. 93 | local param_nodes = {} 94 | 95 | if old_state then 96 | nodes[2] = i(1, old_state.descr:get_text()) 97 | end 98 | param_nodes.descr = nodes[2] 99 | 100 | -- At least one param. 101 | if string.find(args[2][1], ", ") then 102 | vim.list_extend(nodes, { t({ " * ", "" }) }) 103 | end 104 | 105 | local insert = 2 106 | for indx, arg in ipairs(vim.split(args[2][1], ", ", true)) do 107 | -- Get actual name parameter. 108 | arg = vim.split(arg, " ", true)[2] 109 | if arg then 110 | local inode 111 | -- if there was some text in this parameter, use it as static_text for this new snippet. 112 | if old_state and old_state[arg] then 113 | inode = i(insert, old_state["arg" .. arg]:get_text()) 114 | else 115 | inode = i(insert) 116 | end 117 | vim.list_extend( 118 | nodes, 119 | { t({ " * @param " .. arg .. " " }), inode, t({ "", "" }) } 120 | ) 121 | param_nodes["arg" .. arg] = inode 122 | 123 | insert = insert + 1 124 | end 125 | end 126 | 127 | if args[1][1] ~= "void" then 128 | local inode 129 | if old_state and old_state.ret then 130 | inode = i(insert, old_state.ret:get_text()) 131 | else 132 | inode = i(insert) 133 | end 134 | 135 | vim.list_extend( 136 | nodes, 137 | { t({ " * ", " * @return " }), inode, t({ "", "" }) } 138 | ) 139 | param_nodes.ret = inode 140 | insert = insert + 1 141 | end 142 | 143 | if vim.tbl_count(args[3]) ~= 1 then 144 | local exc = string.gsub(args[3][2], " throws ", "") 145 | local ins 146 | if old_state and old_state.ex then 147 | ins = i(insert, old_state.ex:get_text()) 148 | else 149 | ins = i(insert) 150 | end 151 | vim.list_extend( 152 | nodes, 153 | { t({ " * ", " * @throws " .. exc .. " " }), ins, t({ "", "" }) } 154 | ) 155 | param_nodes.ex = ins 156 | insert = insert + 1 157 | end 158 | 159 | vim.list_extend(nodes, { t({ " */" }) }) 160 | 161 | local snip = sn(nil, nodes) 162 | -- Error on attempting overwrite. 163 | snip.old_state = param_nodes 164 | return snip 165 | end 166 | 167 | -- Make sure to not pass an invalid command, as io.popen() may write over nvim-text. 168 | local function bash(_, _, command) 169 | local file = io.popen(command, "r") 170 | local res = {} 171 | for line in file:lines() do 172 | table.insert(res, line) 173 | end 174 | return res 175 | end 176 | 177 | -- Returns a snippet_node wrapped around an insert_node whose initial 178 | -- text value is set to the current date in the desired format. 179 | local date_input = function(args, snip, old_state, fmt) 180 | local fmt = fmt or "%Y-%m-%d" 181 | return sn(nil, i(1, os.date(fmt))) 182 | end 183 | 184 | ls.snippets = { 185 | -- When trying to expand a snippet, luasnip first searches the tables for 186 | -- each filetype specified in 'filetype' followed by 'all'. 187 | -- If ie. the filetype is 'lua.c' 188 | -- - luasnip.lua 189 | -- - luasnip.c 190 | -- - luasnip.all 191 | -- are searched in that order. 192 | all = { 193 | -- trigger is `fn`, second argument to snippet-constructor are the nodes to insert into the buffer on expansion. 194 | s("fn", { 195 | -- Simple static text. 196 | t("//Parameters: "), 197 | -- function, first parameter is the function, second the Placeholders 198 | -- whose text it gets as input. 199 | f(copy, 2), 200 | t({ "", "function " }), 201 | -- Placeholder/Insert. 202 | i(1), 203 | t("("), 204 | -- Placeholder with initial text. 205 | i(2, "int foo"), 206 | -- Linebreak 207 | t({ ") {", "\t" }), 208 | -- Last Placeholder, exit Point of the snippet. 209 | i(0), 210 | t({ "", "}" }), 211 | }), 212 | s("class", { 213 | -- Choice: Switch between two different Nodes, first parameter is its position, second a list of nodes. 214 | c(1, { 215 | t("public "), 216 | t("private "), 217 | }), 218 | t("class "), 219 | i(2), 220 | t(" "), 221 | c(3, { 222 | t("{"), 223 | -- sn: Nested Snippet. Instead of a trigger, it has a position, just like insert-nodes. !!! These don't expect a 0-node!!!! 224 | -- Inside Choices, Nodes don't need a position as the choice node is the one being jumped to. 225 | sn(nil, { 226 | t("extends "), 227 | -- restoreNode: stores and restores nodes. 228 | -- pass position, store-key and nodes. 229 | r(1, "other_class", i(1)), 230 | t(" {"), 231 | }), 232 | sn(nil, { 233 | t("implements "), 234 | -- no need to define the nodes for a given key a second time. 235 | r(1, "other_class"), 236 | t(" {"), 237 | }), 238 | }), 239 | t({ "", "\t" }), 240 | i(0), 241 | t({ "", "}" }), 242 | }), 243 | -- Alternative printf-like notation for defining snippets. It uses format 244 | -- string with placeholders similar to the ones used with Python's .format(). 245 | s( 246 | "fmt1", 247 | fmt("To {title} {} {}.", { 248 | i(2, "Name"), 249 | i(3, "Surname"), 250 | title = c(1, { t("Mr."), t("Ms.") }), 251 | }) 252 | ), 253 | -- To escape delimiters use double them, e.g. `{}` -> `{{}}`. 254 | -- Multi-line format strings by default have empty first/last line removed. 255 | -- Indent common to all lines is also removed. Use the third `opts` argument 256 | -- to control this behaviour. 257 | s( 258 | "fmt2", 259 | fmt( 260 | [[ 261 | foo({1}, {3}) {{ 262 | return {2} * {4} 263 | }} 264 | ]], 265 | { 266 | i(1, "x"), 267 | rep(1), 268 | i(2, "y"), 269 | rep(2), 270 | } 271 | ) 272 | ), 273 | -- Empty placeholders are numbered automatically starting from 1 or the last 274 | -- value of a numbered placeholder. Named placeholders do not affect numbering. 275 | s( 276 | "fmt3", 277 | fmt("{} {a} {} {1} {}", { 278 | t("1"), 279 | t("2"), 280 | a = t("A"), 281 | }) 282 | ), 283 | -- The delimiters can be changed from the default `{}` to something else. 284 | s( 285 | "fmt4", 286 | fmt("foo() { return []; }", i(1, "x"), { delimiters = "[]" }) 287 | ), 288 | -- `fmta` is a convenient wrapper that uses `<>` instead of `{}`. 289 | s("fmt5", fmta("foo() { return <>; }", i(1, "x"))), 290 | -- By default all args must be used. Use strict=false to disable the check 291 | s( 292 | "fmt6", 293 | fmt("use {} only", { t("this"), t("not this") }, { strict = false }) 294 | ), 295 | -- Use a dynamic_node to interpolate the output of a 296 | -- function (see date_input above) into the initial 297 | -- value of an insert_node. 298 | s("novel", { 299 | t("It was a dark and stormy night on "), 300 | d(1, date_input, {}, { user_args = { "%A, %B %d of %Y" } }), 301 | t(" and the clocks were striking thirteen."), 302 | }), 303 | -- Parsing snippets: First parameter: Snippet-Trigger, Second: Snippet body. 304 | -- Placeholders are parsed into choices with 1. the placeholder text(as a snippet) and 2. an empty string. 305 | -- This means they are not SELECTed like in other editors/Snippet engines. 306 | ls.parser.parse_snippet( 307 | "lspsyn", 308 | "Wow! This ${1:Stuff} really ${2:works. ${3:Well, a bit.}}" 309 | ), 310 | 311 | -- When wordTrig is set to false, snippets may also expand inside other words. 312 | ls.parser.parse_snippet( 313 | { trig = "te", wordTrig = false }, 314 | "${1:cond} ? ${2:true} : ${3:false}" 315 | ), 316 | 317 | -- When regTrig is set, trig is treated like a pattern, this snippet will expand after any number. 318 | ls.parser.parse_snippet({ trig = "%d", regTrig = true }, "A Number!!"), 319 | -- Using the condition, it's possible to allow expansion only in specific cases. 320 | s("cond", { 321 | t("will only expand in c-style comments"), 322 | }, { 323 | condition = function(line_to_cursor, matched_trigger, captures) 324 | -- optional whitespace followed by // 325 | return line_to_cursor:match("%s*//") 326 | end, 327 | }), 328 | -- there's some built-in conditions in "luasnip.extras.expand_conditions". 329 | s("cond2", { 330 | t("will only expand at the beginning of the line"), 331 | }, { 332 | condition = conds.line_begin, 333 | }), 334 | -- The last entry of args passed to the user-function is the surrounding snippet. 335 | s( 336 | { trig = "a%d", regTrig = true }, 337 | f(function(_, snip) 338 | return "Triggered with " .. snip.trigger .. "." 339 | end, {}) 340 | ), 341 | -- It's possible to use capture-groups inside regex-triggers. 342 | s( 343 | { trig = "b(%d)", regTrig = true }, 344 | f(function(_, snip) 345 | return "Captured Text: " .. snip.captures[1] .. "." 346 | end, {}) 347 | ), 348 | s({ trig = "c(%d+)", regTrig = true }, { 349 | t("will only expand for even numbers"), 350 | }, { 351 | condition = function(line_to_cursor, matched_trigger, captures) 352 | return tonumber(captures[1]) % 2 == 0 353 | end, 354 | }), 355 | -- Use a function to execute any shell command and print its text. 356 | s("bash", f(bash, {}, "ls")), 357 | -- Short version for applying String transformations using function nodes. 358 | s("transform", { 359 | i(1, "initial text"), 360 | t({ "", "" }), 361 | -- lambda nodes accept an l._1,2,3,4,5, which in turn accept any string transformations. 362 | -- This list will be applied in order to the first node given in the second argument. 363 | l(l._1:match("[^i]*$"):gsub("i", "o"):gsub(" ", "_"):upper(), 1), 364 | }), 365 | 366 | s("transform2", { 367 | i(1, "initial text"), 368 | t("::"), 369 | i(2, "replacement for e"), 370 | t({ "", "" }), 371 | -- Lambdas can also apply transforms USING the text of other nodes: 372 | l(l._1:gsub("e", l._2), { 1, 2 }), 373 | }), 374 | s({ trig = "trafo(%d+)", regTrig = true }, { 375 | -- env-variables and captures can also be used: 376 | l(l.CAPTURE1:gsub("1", l.TM_FILENAME), {}), 377 | }), 378 | -- Set store_selection_keys = "" (for example) in your 379 | -- luasnip.config.setup() call to populate 380 | -- TM_SELECTED_TEXT/SELECT_RAW/SELECT_DEDENT. 381 | -- In this case: select a URL, hit Tab, then expand this snippet. 382 | s("link_url", { 383 | t(''), 390 | i(1), 391 | t(""), 392 | i(0), 393 | }), 394 | -- Shorthand for repeating the text in a given node. 395 | s("repeat", { i(1, "text"), t({ "", "" }), rep(1) }), 396 | -- Directly insert the ouput from a function evaluated at runtime. 397 | s("part", p(os.date, "%Y")), 398 | -- use matchNodes (`m(argnode, condition, then, else)`) to insert text 399 | -- based on a pattern/function/lambda-evaluation. 400 | -- It's basically a shortcut for simple functionNodes: 401 | s("mat", { 402 | i(1, { "sample_text" }), 403 | t(": "), 404 | m(1, "%d", "contains a number", "no number :("), 405 | }), 406 | -- The `then`-text defaults to the first capture group/the entire 407 | -- match if there are none. 408 | s("mat2", { 409 | i(1, { "sample_text" }), 410 | t(": "), 411 | m(1, "[abc][abc][abc]"), 412 | }), 413 | -- It is even possible to apply gsubs' or other transformations 414 | -- before matching. 415 | s("mat3", { 416 | i(1, { "sample_text" }), 417 | t(": "), 418 | m( 419 | 1, 420 | l._1:gsub("[123]", ""):match("%d"), 421 | "contains a number that isn't 1, 2 or 3!" 422 | ), 423 | }), 424 | -- `match` also accepts a function in place of the condition, which in 425 | -- turn accepts the usual functionNode-args. 426 | -- The condition is considered true if the function returns any 427 | -- non-nil/false-value. 428 | -- If that value is a string, it is used as the `if`-text if no if is explicitly given. 429 | s("mat4", { 430 | i(1, { "sample_text" }), 431 | t(": "), 432 | m(1, function(args) 433 | -- args is a table of multiline-strings (as usual). 434 | return (#args[1][1] % 2 == 0 and args[1]) or nil 435 | end), 436 | }), 437 | -- The nonempty-node inserts text depending on whether the arg-node is 438 | -- empty. 439 | s("nempty", { 440 | i(1, "sample_text"), 441 | n(1, "i(1) is not empty!"), 442 | }), 443 | -- dynamic lambdas work exactly like regular lambdas, except that they 444 | -- don't return a textNode, but a dynamicNode containing one insertNode. 445 | -- This makes it easier to dynamically set preset-text for insertNodes. 446 | s("dl1", { 447 | i(1, "sample_text"), 448 | t({ ":", "" }), 449 | dl(2, l._1, 1), 450 | }), 451 | -- Obviously, it's also possible to apply transformations, just like lambdas. 452 | s("dl2", { 453 | i(1, "sample_text"), 454 | i(2, "sample_text_2"), 455 | t({ "", "" }), 456 | dl(3, l._1:gsub("\n", " linebreak ") .. l._2, { 1, 2 }), 457 | }), 458 | }, 459 | java = { 460 | -- Very long example for a java class. 461 | s("fn", { 462 | d(6, jdocsnip, { 2, 4, 5 }), 463 | t({ "", "" }), 464 | c(1, { 465 | t("public "), 466 | t("private "), 467 | }), 468 | c(2, { 469 | t("void"), 470 | t("String"), 471 | t("char"), 472 | t("int"), 473 | t("double"), 474 | t("boolean"), 475 | i(nil, ""), 476 | }), 477 | t(" "), 478 | i(3, "myFunc"), 479 | t("("), 480 | i(4), 481 | t(")"), 482 | c(5, { 483 | t(""), 484 | sn(nil, { 485 | t({ "", " throws " }), 486 | i(1), 487 | }), 488 | }), 489 | t({ " {", "\t" }), 490 | i(0), 491 | t({ "", "}" }), 492 | }), 493 | }, 494 | tex = { 495 | -- rec_ls is self-referencing. That makes this snippet 'infinite' eg. have as many 496 | -- \item as necessary by utilizing a choiceNode. 497 | s("ls", { 498 | t({ "\\begin{itemize}", "\t\\item " }), 499 | i(1), 500 | d(2, rec_ls, {}), 501 | t({ "", "\\end{itemize}" }), 502 | }), 503 | }, 504 | } 505 | 506 | -- autotriggered snippets have to be defined in a separate table, luasnip.autosnippets. 507 | ls.autosnippets = { 508 | all = { 509 | s("autotrigger", { 510 | t("autosnippet"), 511 | }), 512 | }, 513 | } 514 | 515 | -- in a lua file: search lua-, then c-, then all-snippets. 516 | ls.filetype_extend("lua", { "c" }) 517 | -- in a cpp file: search c-snippets, then all-snippets only (no cpp-snippets!!). 518 | ls.filetype_set("cpp", { "c" }) 519 | 520 | -- Beside defining your own snippets you can also load snippets from "vscode-like" packages 521 | -- that expose snippets in json files, for example . 522 | -- Mind that this will extend `ls.snippets` so you need to do it after your own snippets or you 523 | -- will need to extend the table yourself instead of setting a new one. 524 | 525 | require("luasnip.loaders.from_vscode").load({ include = { "python" } }) -- Load only python snippets 526 | 527 | -- The directories will have to be structured like eg. (include 528 | -- a similar `package.json`) 529 | require("luasnip.loaders.from_vscode").load({ paths = { "./my-snippets" } }) -- Load snippets from my-snippets folder 530 | 531 | -- You can also use lazy loading so snippets are loaded on-demand, not all at once (may interfere with lazy-loading luasnip itself). 532 | require("luasnip.loaders.from_vscode").lazy_load() -- You can pass { paths = "./my-snippets/"} as well 533 | 534 | -- You can also use snippets in snipmate format, for example . 535 | -- The usage is similar to vscode. 536 | 537 | -- One peculiarity of honza/vim-snippets is that the file with the global snippets is _.snippets, so global snippets 538 | -- are stored in `ls.snippets._`. 539 | -- We need to tell luasnip that "_" contains global snippets: 540 | ls.filetype_extend("all", { "_" }) 541 | 542 | require("luasnip.loaders.from_snipmate").load({ include = { "c" } }) -- Load only python snippets 543 | 544 | require("luasnip.loaders.from_snipmate").load({ path = { "./my-snippets" } }) -- Load snippets from my-snippets folder 545 | -- If path is not specified, luasnip will look for the `snippets` directory in rtp (for custom-snippet probably 546 | -- `~/.config/nvim/snippets`). 547 | 548 | require("luasnip.loaders.from_snipmate").lazy_load() -- Lazy loading 549 | 550 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 从零开始搭建Neovim Java IDE开发环境 2 | 3 | @[toc] 4 | 5 | # 系统环境 6 | 理论上这套环境是支持跨平台的,macOS,Linux,Windows都支持。为了防止一些微小的差异,这里我把我的环境信息说明一下。 7 | OS: Ubuntu 20.04 LTS x86_64 8 | CPU: Intel Xeon E5-2680 v4 (2) @ 2.394GHz 9 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/d53f8732f945458ba9e3cc498c6d85a8.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASVRLRVlf,size_20,color_FFFFFF,t_70,g_se,x_16) 10 | 11 | 这是我云服务器上的环境,没有什么特别的。 12 | 13 | # 准备工作 14 | ## 文件下载 15 | 为了方便大家,我把本文中用于的所有文件。都上传到网盘供大家使用。 16 | 17 | 链接: [https://pan.baidu.com/s/1ngwkOpclgLCKUW_YFX-WOg?pwd=g8fu](https://pan.baidu.com/s/1ngwkOpclgLCKUW_YFX-WOg?pwd=g8fu) 提取码: `g8fu` 18 | 19 | 文件说明: 20 | - nvim-linux64.tar.gz 是neovim0.61的安装包 21 | - jdk-11.0.13_linux-x64_bin.tar.gz 是JDK的压缩包 22 | - jdt-language-server-1.9.0-202203031534.tar.gz用于智能提示的插件 23 | 24 | ## 安装neovim 0.6以上版本 25 | 采用任意一种方法都可以,只有一个要求neovim版本要0.6以上。 26 | 通用方法: 27 | 直接从github官网下载 28 | [https://github.com/neovim/neovim/releases](https://github.com/neovim/neovim/releases) 29 | 30 | 考虑到github比较慢,所以可以使用CSDN的镜像进行下载。 31 | [https://gitcode.net/mirrors/neovim/neovim](https://gitcode.net/mirrors/neovim/neovim) 32 | 33 | 下载后安装示例: 34 | 35 | ```bash 36 | ##解压 37 | tar -xvf nvim-linux64.tar.gz 38 | mv nvim-linux64 /usr/local/ 39 | ##创建软链接 40 | ln -s /usr/local/nvim-linux64/bin/nvim /bin/nvim 41 | ``` 42 | 43 | 44 | 45 | # 下载解压jdt-language-server 46 | 47 | 下载jdt-language-server 48 | 不同版本下载导航 49 | [https://download.eclipse.org/jdtls/milestones/?d](https://download.eclipse.org/jdtls/milestones/?d) 50 | 我最终下载的版本是: 51 | 52 | [https://download.eclipse.org/jdtls/milestones/1.9.0/jdt-language-server-1.9.0-202203031534.tar.gz](https://download.eclipse.org/jdtls/milestones/1.9.0/jdt-language-server-1.9.0-202203031534.tar.gz) 53 | 54 | 以下我的路径是个人喜好,可以根据自己的实际情况修改保存路径: 55 | ```bash 56 | #创建workspace目录,后面会用到 57 | mkdir -p ~/.local/share/nvim/lsp/jdt-language-server/workspace/folder 58 | cd ~/.local/share/nvim/lsp/jdt-language-server 59 | # 下载jdt-language-server-xxxxx.tar.gz 60 | wget https://download.eclipse.org/jdtls/milestones/1.9.0/jdt-language-server-1.9.0-202203031534.tar.gz 61 | # 解压 62 | tar -zxvf jdt-language-server-1.9.0-202203031534.tar.gz 63 | ``` 64 | 我的目录结构如下图所示 65 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/05d58a9c52c4458dbed9275a33e1ad83.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASVRLRVlf,size_20,color_FFFFFF,t_70,g_se,x_16) 66 | 67 | 68 | ## 安装JDK11 69 | JDK版本选择,这里有一个小坑,就是JDK的版本要选择JDK11及以上版本才行。因为就目前来看,JDK8使用的概率还是非常高的。 70 | 71 | 如果你使用JDK8,使用java文件会报如下的错误: 72 | ***Client 1 quit with exit code 1 and signal 0*** 73 | 74 | **推荐使用JDK11**,因为我实测JDK11是正常使用的,其他版本的JDK我没有一一测试。 75 | 我的版本信息如下: 76 | 77 | ```bash 78 | java -version 79 | java version "11.0.13" 2021-10-19 LTS 80 | Java(TM) SE Runtime Environment 18.9 (build 11.0.13+10-LTS-370) 81 | Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.13+10-LTS-370, mixed mode) 82 | ``` 83 | 环境变量设置参考: 84 | 85 | ```bash 86 | export JAVA_HOME=/root/neovim-IDE-soft/jdk-11.0.13 #JDK的主目录,建议使用JDK11,使用JDK8会报错 87 | PATH=$PATH:$JAVA_HOME/bin 88 | export JDTLS_HOME=$HOME/.local/share/nvim/lsp/jdt-language-server/ # 包含 plugin 和 configs 的目录,由jdt-language-server-xxx.tar.gz解压出的 89 | export WORKSPACE=$HOME/.local/share/nvim/lsp/jdt-language-server/workspace/ # 不设置则默认是$HOME/workspace 90 | ``` 91 | ## 安装curl git 92 | curl git这两个软件,很多系统上默认安装了,我在这里提一下,因为在使用nvim安装插件时会用到,特别是git。 93 | 94 | ubuntu中这样安装,其他系统安装方法自己百度。 95 | ```bash 96 | apt-get install -y curl git 97 | ``` 98 | 99 | 100 | 至此准备工作完成了✅ 101 | 102 | # neovim配置 103 | ## 插件安装 104 | ```bash 105 | #创建nvim用到的目录 106 | mkdir -p ~/.config/nvim/lua 107 | #创建插件管理器的配置文件 108 | nvim ~/.config/nvim/lua/plugins.lua 109 | ``` 110 | `~/.config/nvim/lua/plugins.lua`文件内容如下: 111 | 112 | ```lua 113 | ---@diagnostic disable: undefined-global 114 | --在没有安装packer的电脑上,自动安装packer插件 115 | local fn = vim.fn 116 | local install_path = fn.stdpath("data") .. "/site/pack/packer/start/packer.nvim" 117 | if fn.empty(fn.glob(install_path)) > 0 then 118 | fn.system( 119 | {"git", "clone", "--depth", "1", "https://gitcode.net/mirrors/wbthomason/packer.nvim", install_path} 120 | ) --csdn加速镜像 121 | vim.cmd "packadd packer.nvim" 122 | end 123 | -- Only required if you have packer configured as `opt` 124 | --【国内加速】插件名称超长的说明: 125 | --由于国内网络环境访问github及其不稳定,所以如果在gitcode.net上的镜像的(https://gitcode.net/mirrors/开头的),我们尽量使用。这样可以提高访问速度。 126 | --gitcode.net没有镜像的部分(https://gitcode.net/lxyoucan开头的),是我手动clone到gitcode上的不定期更新。 127 | --如果你访问github比较流畅,插件名称只保留后两段即如:neovim/nvim-lspconfig 128 | vim.cmd [[packadd packer.nvim]] 129 | return require("packer").startup(function() 130 | -- Packer可以管理自己的更新 131 | use "https://gitcode.net/mirrors/wbthomason/packer.nvim" 132 | --Nvim LSP 客户端的快速入门配置 133 | use "https://gitcode.net/mirrors/neovim/nvim-lspconfig" 134 | --自动提示插件 135 | use { 136 | "https://gitcode.net/mirrors/hrsh7th/nvim-cmp", 137 | requires = { 138 | "https://gitcode.net/lxyoucan/cmp-nvim-lsp", --neovim 内置 LSP 客户端的 nvim-cmp 源 139 | "https://gitcode.net/lxyoucan/cmp-buffer", --从buffer中智能提示 140 | "https://gitcode.net/lxyoucan/cmp-path" --自动提示硬盘上的文件 141 | } 142 | } 143 | -- java语言支持 144 | use "https://gitcode.net/lxyoucan/nvim-jdtls.git" 145 | -- 代码段提示 146 | use { 147 | "https://gitcode.net/mirrors/L3MON4D3/LuaSnip", 148 | requires = { 149 | "https://gitcode.net/lxyoucan/cmp_luasnip", -- Snippets source for nvim-cmp 150 | "https://gitcode.net/lxyoucan/friendly-snippets" --代码段合集 151 | } 152 | } 153 | --主题安装 154 | use "https://gitcode.net/mirrors/sainnhe/gruvbox-material" 155 | end) 156 | 157 | ``` 158 | 配置主配置文件: 159 | ```bash 160 | nvim ~/.config/nvim/init.lua 161 | ``` 162 | 添加内容如下: 163 | 164 | ```bash 165 | --插件管理器 166 | require("plugins") 167 | --主题设置 168 | vim.cmd("colorscheme " .. "gruvbox-material") 169 | ------按键映射 start------ 170 | local opts = {noremap = true, silent = true} 171 | local keymap = vim.api.nvim_set_keymap 172 | --把空格键设置成 173 | vim.g.mapleader = " " 174 | --快速跳转行首与行尾 175 | keymap('n', 'L', '$', opts) 176 | keymap('v', 'L', '$', opts) 177 | keymap('n', 'H', '^', opts) 178 | keymap('v', 'H', '^', opts) 179 | --插入模式jk当Esc 180 | keymap('i', 'jk', '', opts) 181 | --保 存 182 | keymap('n', '', ':w', opts) 183 | keymap('i', '', ' :w', opts) 184 | --全选 185 | keymap('n', '', 'ggG', opts) 186 | ------按键映射 end ------ 187 | -- 文件编码格式 188 | vim.opt.fileencoding = "utf-8" 189 | -- 显示行号 190 | vim.opt.number=true 191 | -- tab=4个空格 192 | vim.opt.tabstop=4 193 | vim.opt.shiftwidth=4 194 | 195 | 196 | ``` 197 | 保存后,重新打开nvim。执行`:PackerInstall`,如下图所示: 198 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/c03e3e0a60d1490d9df3a166f9f13b84.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASVRLRVlf,size_20,color_FFFFFF,t_70,g_se,x_16) 199 | 插件安装成功,界面如下: 200 | 201 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/2e82bf4ff07b4899b03ab335e8ef8859.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASVRLRVlf,size_20,color_FFFFFF,t_70,g_se,x_16) 202 | 这样我们就完成了,Java开发所需要的核心插件的安装了。 203 | 204 | 205 | ```lua 206 | -- Packer可以管理自己的更新 207 | use "wbthomason/packer.nvim" 208 | --Nvim LSP 客户端的快速入门配置 209 | use "neovim/nvim-lspconfig" 210 | --自动提示插件 211 | use { 212 | "hrsh7th/nvim-cmp", 213 | requires = { 214 | "hrsh7th/cmp-nvim-lsp", --neovim 内置 LSP 客户端的 nvim-cmp 源 215 | "hrsh7th/cmp-buffer", --从buffer中智能提示 216 | "hrsh7th/cmp-path" --自动提示硬盘上的文件 217 | } 218 | } 219 | -- 代码段提示 220 | use { 221 | "https://gitcode.net/mirrors/L3MON4D3/LuaSnip", 222 | requires = { 223 | "saadparwaiz1/cmp_luasnip", -- Snippets source for nvim-cmp 224 | "rafamadriz/friendly-snippets" --代码段合集 225 | } 226 | } 227 | 228 | -- java语言支持jdtls扩展插件,在lsp基础上扩展了一些实用的内容 229 | use "mfussenegger/nvim-jdtls" 230 | 231 | ``` 232 | ## 配置nvim-cmp 233 | 234 | ```lua 235 | #创建plugin配置目录 236 | mkdir -p ~/.config/nvim/after/plugin 237 | #编辑nvim-cmp配置文件 238 | nvim ~/.config/nvim/after/plugin/nvim-cmp.lua 239 | ``` 240 | ~/.config/nvim/after/plugin/nvim-cmp.lua文件内容如下: 241 | 242 | ```lua 243 | local status, nvim_lsp = pcall(require, "lspconfig") 244 | if (not status) then 245 | return 246 | end 247 | 248 | -- Set completeopt to have a better completion experience 249 | vim.o.completeopt = "menuone,noselect" 250 | 251 | -- luasnip setup 252 | local luasnip = require "luasnip" 253 | 254 | -- nvim-cmp setup 255 | local cmp = require "cmp" 256 | cmp.setup { 257 | snippet = { 258 | expand = function(args) 259 | require("luasnip").lsp_expand(args.body) 260 | end 261 | }, 262 | mapping = { 263 | [""] = cmp.mapping.select_prev_item(), 264 | [""] = cmp.mapping.select_next_item(), 265 | [""] = cmp.mapping.scroll_docs(-4), 266 | [""] = cmp.mapping.scroll_docs(4), 267 | [""] = cmp.mapping.complete(), 268 | [""] = cmp.mapping.close(), 269 | [""] = cmp.mapping.confirm { 270 | behavior = cmp.ConfirmBehavior.Replace, 271 | select = false 272 | }, 273 | [""] = function(fallback) 274 | if cmp.visible() then 275 | cmp.select_next_item() 276 | elseif luasnip.expand_or_jumpable() then 277 | vim.fn.feedkeys(vim.api.nvim_replace_termcodes("luasnip-expand-or-jump", true, true, true), "") 278 | else 279 | fallback() 280 | end 281 | end, 282 | [""] = function(fallback) 283 | if cmp.visible() then 284 | cmp.select_prev_item() 285 | elseif luasnip.jumpable(-1) then 286 | vim.fn.feedkeys(vim.api.nvim_replace_termcodes("luasnip-jump-prev", true, true, true), "") 287 | else 288 | fallback() 289 | end 290 | end 291 | }, 292 | sources = { 293 | {name = "nvim_lsp"}, 294 | {name = "luasnip"}, 295 | { 296 | name = "buffer", 297 | option = { 298 | get_bufnrs = function() 299 | return vim.api.nvim_list_bufs() 300 | end 301 | } 302 | }, 303 | {name = "path"} 304 | } 305 | } 306 | 307 | ``` 308 | ## 配置LuaSnip 309 | 310 | ```lua 311 | nvim ~/.config/nvim/after/plugin/snippets.lua 312 | ``` 313 | 内容如下: 314 | 315 | ```lua 316 | local ls = require("luasnip") 317 | -- some shorthands... 318 | local s = ls.snippet 319 | local sn = ls.snippet_node 320 | local t = ls.text_node 321 | local i = ls.insert_node 322 | local f = ls.function_node 323 | local c = ls.choice_node 324 | local d = ls.dynamic_node 325 | local r = ls.restore_node 326 | local l = require("luasnip.extras").lambda 327 | local rep = require("luasnip.extras").rep 328 | local p = require("luasnip.extras").partial 329 | local m = require("luasnip.extras").match 330 | local n = require("luasnip.extras").nonempty 331 | local dl = require("luasnip.extras").dynamic_lambda 332 | local fmt = require("luasnip.extras.fmt").fmt 333 | local fmta = require("luasnip.extras.fmt").fmta 334 | local types = require("luasnip.util.types") 335 | local conds = require("luasnip.extras.expand_conditions") 336 | 337 | -- If you're reading this file for the first time, best skip to around line 190 338 | -- where the actual snippet-definitions start. 339 | 340 | -- Every unspecified option will be set to the default. 341 | ls.config.set_config({ 342 | history = true, 343 | -- Update more often, :h events for more info. 344 | updateevents = "TextChanged,TextChangedI", 345 | -- Snippets aren't automatically removed if their text is deleted. 346 | -- `delete_check_events` determines on which events (:h events) a check for 347 | -- deleted snippets is performed. 348 | -- This can be especially useful when `history` is enabled. 349 | delete_check_events = "TextChanged", 350 | ext_opts = { 351 | [types.choiceNode] = { 352 | active = { 353 | virt_text = { { "choiceNode", "Comment" } }, 354 | }, 355 | }, 356 | }, 357 | -- treesitter-hl has 100, use something higher (default is 200). 358 | ext_base_prio = 300, 359 | -- minimal increase in priority. 360 | ext_prio_increase = 1, 361 | enable_autosnippets = true, 362 | -- mapping for cutting selected text so it's usable as SELECT_DEDENT, 363 | -- SELECT_RAW or TM_SELECTED_TEXT (mapped via xmap). 364 | store_selection_keys = "", 365 | -- luasnip uses this function to get the currently active filetype. This 366 | -- is the (rather uninteresting) default, but it's possible to use 367 | -- eg. treesitter for getting the current filetype by setting ft_func to 368 | -- require("luasnip.extras.filetype_functions").from_cursor (requires 369 | -- `nvim-treesitter/nvim-treesitter`). This allows correctly resolving 370 | -- the current filetype in eg. a markdown-code block or `vim.cmd()`. 371 | ft_func = function() 372 | return vim.split(vim.bo.filetype, ".", true) 373 | end, 374 | }) 375 | 376 | -- args is a table, where 1 is the text in Placeholder 1, 2 the text in 377 | -- placeholder 2,... 378 | local function copy(args) 379 | return args[1] 380 | end 381 | 382 | -- 'recursive' dynamic snippet. Expands to some text followed by itself. 383 | local rec_ls 384 | rec_ls = function() 385 | return sn( 386 | nil, 387 | c(1, { 388 | -- Order is important, sn(...) first would cause infinite loop of expansion. 389 | t(""), 390 | sn(nil, { t({ "", "\t\\item " }), i(1), d(2, rec_ls, {}) }), 391 | }) 392 | ) 393 | end 394 | 395 | -- complicated function for dynamicNode. 396 | local function jdocsnip(args, _, old_state) 397 | -- !!! old_state is used to preserve user-input here. DON'T DO IT THAT WAY! 398 | -- Using a restoreNode instead is much easier. 399 | -- View this only as an example on how old_state functions. 400 | local nodes = { 401 | t({ "/**", " * " }), 402 | i(1, "A short Description"), 403 | t({ "", "" }), 404 | } 405 | 406 | -- These will be merged with the snippet; that way, should the snippet be updated, 407 | -- some user input eg. text can be referred to in the new snippet. 408 | local param_nodes = {} 409 | 410 | if old_state then 411 | nodes[2] = i(1, old_state.descr:get_text()) 412 | end 413 | param_nodes.descr = nodes[2] 414 | 415 | -- At least one param. 416 | if string.find(args[2][1], ", ") then 417 | vim.list_extend(nodes, { t({ " * ", "" }) }) 418 | end 419 | 420 | local insert = 2 421 | for indx, arg in ipairs(vim.split(args[2][1], ", ", true)) do 422 | -- Get actual name parameter. 423 | arg = vim.split(arg, " ", true)[2] 424 | if arg then 425 | local inode 426 | -- if there was some text in this parameter, use it as static_text for this new snippet. 427 | if old_state and old_state[arg] then 428 | inode = i(insert, old_state["arg" .. arg]:get_text()) 429 | else 430 | inode = i(insert) 431 | end 432 | vim.list_extend( 433 | nodes, 434 | { t({ " * @param " .. arg .. " " }), inode, t({ "", "" }) } 435 | ) 436 | param_nodes["arg" .. arg] = inode 437 | 438 | insert = insert + 1 439 | end 440 | end 441 | 442 | if args[1][1] ~= "void" then 443 | local inode 444 | if old_state and old_state.ret then 445 | inode = i(insert, old_state.ret:get_text()) 446 | else 447 | inode = i(insert) 448 | end 449 | 450 | vim.list_extend( 451 | nodes, 452 | { t({ " * ", " * @return " }), inode, t({ "", "" }) } 453 | ) 454 | param_nodes.ret = inode 455 | insert = insert + 1 456 | end 457 | 458 | if vim.tbl_count(args[3]) ~= 1 then 459 | local exc = string.gsub(args[3][2], " throws ", "") 460 | local ins 461 | if old_state and old_state.ex then 462 | ins = i(insert, old_state.ex:get_text()) 463 | else 464 | ins = i(insert) 465 | end 466 | vim.list_extend( 467 | nodes, 468 | { t({ " * ", " * @throws " .. exc .. " " }), ins, t({ "", "" }) } 469 | ) 470 | param_nodes.ex = ins 471 | insert = insert + 1 472 | end 473 | 474 | vim.list_extend(nodes, { t({ " */" }) }) 475 | 476 | local snip = sn(nil, nodes) 477 | -- Error on attempting overwrite. 478 | snip.old_state = param_nodes 479 | return snip 480 | end 481 | 482 | -- Make sure to not pass an invalid command, as io.popen() may write over nvim-text. 483 | local function bash(_, _, command) 484 | local file = io.popen(command, "r") 485 | local res = {} 486 | for line in file:lines() do 487 | table.insert(res, line) 488 | end 489 | return res 490 | end 491 | 492 | -- Returns a snippet_node wrapped around an insert_node whose initial 493 | -- text value is set to the current date in the desired format. 494 | local date_input = function(args, snip, old_state, fmt) 495 | local fmt = fmt or "%Y-%m-%d" 496 | return sn(nil, i(1, os.date(fmt))) 497 | end 498 | 499 | ls.snippets = { 500 | -- When trying to expand a snippet, luasnip first searches the tables for 501 | -- each filetype specified in 'filetype' followed by 'all'. 502 | -- If ie. the filetype is 'lua.c' 503 | -- - luasnip.lua 504 | -- - luasnip.c 505 | -- - luasnip.all 506 | -- are searched in that order. 507 | all = { 508 | -- trigger is `fn`, second argument to snippet-constructor are the nodes to insert into the buffer on expansion. 509 | s("fn", { 510 | -- Simple static text. 511 | t("//Parameters: "), 512 | -- function, first parameter is the function, second the Placeholders 513 | -- whose text it gets as input. 514 | f(copy, 2), 515 | t({ "", "function " }), 516 | -- Placeholder/Insert. 517 | i(1), 518 | t("("), 519 | -- Placeholder with initial text. 520 | i(2, "int foo"), 521 | -- Linebreak 522 | t({ ") {", "\t" }), 523 | -- Last Placeholder, exit Point of the snippet. 524 | i(0), 525 | t({ "", "}" }), 526 | }), 527 | s("class", { 528 | -- Choice: Switch between two different Nodes, first parameter is its position, second a list of nodes. 529 | c(1, { 530 | t("public "), 531 | t("private "), 532 | }), 533 | t("class "), 534 | i(2), 535 | t(" "), 536 | c(3, { 537 | t("{"), 538 | -- sn: Nested Snippet. Instead of a trigger, it has a position, just like insert-nodes. !!! These don't expect a 0-node!!!! 539 | -- Inside Choices, Nodes don't need a position as the choice node is the one being jumped to. 540 | sn(nil, { 541 | t("extends "), 542 | -- restoreNode: stores and restores nodes. 543 | -- pass position, store-key and nodes. 544 | r(1, "other_class", i(1)), 545 | t(" {"), 546 | }), 547 | sn(nil, { 548 | t("implements "), 549 | -- no need to define the nodes for a given key a second time. 550 | r(1, "other_class"), 551 | t(" {"), 552 | }), 553 | }), 554 | t({ "", "\t" }), 555 | i(0), 556 | t({ "", "}" }), 557 | }), 558 | -- Alternative printf-like notation for defining snippets. It uses format 559 | -- string with placeholders similar to the ones used with Python's .format(). 560 | s( 561 | "fmt1", 562 | fmt("To {title} {} {}.", { 563 | i(2, "Name"), 564 | i(3, "Surname"), 565 | title = c(1, { t("Mr."), t("Ms.") }), 566 | }) 567 | ), 568 | -- To escape delimiters use double them, e.g. `{}` -> `{{}}`. 569 | -- Multi-line format strings by default have empty first/last line removed. 570 | -- Indent common to all lines is also removed. Use the third `opts` argument 571 | -- to control this behaviour. 572 | s( 573 | "fmt2", 574 | fmt( 575 | [[ 576 | foo({1}, {3}) {{ 577 | return {2} * {4} 578 | }} 579 | ]], 580 | { 581 | i(1, "x"), 582 | rep(1), 583 | i(2, "y"), 584 | rep(2), 585 | } 586 | ) 587 | ), 588 | -- Empty placeholders are numbered automatically starting from 1 or the last 589 | -- value of a numbered placeholder. Named placeholders do not affect numbering. 590 | s( 591 | "fmt3", 592 | fmt("{} {a} {} {1} {}", { 593 | t("1"), 594 | t("2"), 595 | a = t("A"), 596 | }) 597 | ), 598 | -- The delimiters can be changed from the default `{}` to something else. 599 | s( 600 | "fmt4", 601 | fmt("foo() { return []; }", i(1, "x"), { delimiters = "[]" }) 602 | ), 603 | -- `fmta` is a convenient wrapper that uses `<>` instead of `{}`. 604 | s("fmt5", fmta("foo() { return <>; }", i(1, "x"))), 605 | -- By default all args must be used. Use strict=false to disable the check 606 | s( 607 | "fmt6", 608 | fmt("use {} only", { t("this"), t("not this") }, { strict = false }) 609 | ), 610 | -- Use a dynamic_node to interpolate the output of a 611 | -- function (see date_input above) into the initial 612 | -- value of an insert_node. 613 | s("novel", { 614 | t("It was a dark and stormy night on "), 615 | d(1, date_input, {}, { user_args = { "%A, %B %d of %Y" } }), 616 | t(" and the clocks were striking thirteen."), 617 | }), 618 | -- Parsing snippets: First parameter: Snippet-Trigger, Second: Snippet body. 619 | -- Placeholders are parsed into choices with 1. the placeholder text(as a snippet) and 2. an empty string. 620 | -- This means they are not SELECTed like in other editors/Snippet engines. 621 | ls.parser.parse_snippet( 622 | "lspsyn", 623 | "Wow! This ${1:Stuff} really ${2:works. ${3:Well, a bit.}}" 624 | ), 625 | 626 | -- When wordTrig is set to false, snippets may also expand inside other words. 627 | ls.parser.parse_snippet( 628 | { trig = "te", wordTrig = false }, 629 | "${1:cond} ? ${2:true} : ${3:false}" 630 | ), 631 | 632 | -- When regTrig is set, trig is treated like a pattern, this snippet will expand after any number. 633 | ls.parser.parse_snippet({ trig = "%d", regTrig = true }, "A Number!!"), 634 | -- Using the condition, it's possible to allow expansion only in specific cases. 635 | s("cond", { 636 | t("will only expand in c-style comments"), 637 | }, { 638 | condition = function(line_to_cursor, matched_trigger, captures) 639 | -- optional whitespace followed by // 640 | return line_to_cursor:match("%s*//") 641 | end, 642 | }), 643 | -- there's some built-in conditions in "luasnip.extras.expand_conditions". 644 | s("cond2", { 645 | t("will only expand at the beginning of the line"), 646 | }, { 647 | condition = conds.line_begin, 648 | }), 649 | -- The last entry of args passed to the user-function is the surrounding snippet. 650 | s( 651 | { trig = "a%d", regTrig = true }, 652 | f(function(_, snip) 653 | return "Triggered with " .. snip.trigger .. "." 654 | end, {}) 655 | ), 656 | -- It's possible to use capture-groups inside regex-triggers. 657 | s( 658 | { trig = "b(%d)", regTrig = true }, 659 | f(function(_, snip) 660 | return "Captured Text: " .. snip.captures[1] .. "." 661 | end, {}) 662 | ), 663 | s({ trig = "c(%d+)", regTrig = true }, { 664 | t("will only expand for even numbers"), 665 | }, { 666 | condition = function(line_to_cursor, matched_trigger, captures) 667 | return tonumber(captures[1]) % 2 == 0 668 | end, 669 | }), 670 | -- Use a function to execute any shell command and print its text. 671 | s("bash", f(bash, {}, "ls")), 672 | -- Short version for applying String transformations using function nodes. 673 | s("transform", { 674 | i(1, "initial text"), 675 | t({ "", "" }), 676 | -- lambda nodes accept an l._1,2,3,4,5, which in turn accept any string transformations. 677 | -- This list will be applied in order to the first node given in the second argument. 678 | l(l._1:match("[^i]*$"):gsub("i", "o"):gsub(" ", "_"):upper(), 1), 679 | }), 680 | 681 | s("transform2", { 682 | i(1, "initial text"), 683 | t("::"), 684 | i(2, "replacement for e"), 685 | t({ "", "" }), 686 | -- Lambdas can also apply transforms USING the text of other nodes: 687 | l(l._1:gsub("e", l._2), { 1, 2 }), 688 | }), 689 | s({ trig = "trafo(%d+)", regTrig = true }, { 690 | -- env-variables and captures can also be used: 691 | l(l.CAPTURE1:gsub("1", l.TM_FILENAME), {}), 692 | }), 693 | -- Set store_selection_keys = "" (for example) in your 694 | -- luasnip.config.setup() call to populate 695 | -- TM_SELECTED_TEXT/SELECT_RAW/SELECT_DEDENT. 696 | -- In this case: select a URL, hit Tab, then expand this snippet. 697 | s("link_url", { 698 | t(''), 705 | i(1), 706 | t(""), 707 | i(0), 708 | }), 709 | -- Shorthand for repeating the text in a given node. 710 | s("repeat", { i(1, "text"), t({ "", "" }), rep(1) }), 711 | -- Directly insert the ouput from a function evaluated at runtime. 712 | s("part", p(os.date, "%Y")), 713 | -- use matchNodes (`m(argnode, condition, then, else)`) to insert text 714 | -- based on a pattern/function/lambda-evaluation. 715 | -- It's basically a shortcut for simple functionNodes: 716 | s("mat", { 717 | i(1, { "sample_text" }), 718 | t(": "), 719 | m(1, "%d", "contains a number", "no number :("), 720 | }), 721 | -- The `then`-text defaults to the first capture group/the entire 722 | -- match if there are none. 723 | s("mat2", { 724 | i(1, { "sample_text" }), 725 | t(": "), 726 | m(1, "[abc][abc][abc]"), 727 | }), 728 | -- It is even possible to apply gsubs' or other transformations 729 | -- before matching. 730 | s("mat3", { 731 | i(1, { "sample_text" }), 732 | t(": "), 733 | m( 734 | 1, 735 | l._1:gsub("[123]", ""):match("%d"), 736 | "contains a number that isn't 1, 2 or 3!" 737 | ), 738 | }), 739 | -- `match` also accepts a function in place of the condition, which in 740 | -- turn accepts the usual functionNode-args. 741 | -- The condition is considered true if the function returns any 742 | -- non-nil/false-value. 743 | -- If that value is a string, it is used as the `if`-text if no if is explicitly given. 744 | s("mat4", { 745 | i(1, { "sample_text" }), 746 | t(": "), 747 | m(1, function(args) 748 | -- args is a table of multiline-strings (as usual). 749 | return (#args[1][1] % 2 == 0 and args[1]) or nil 750 | end), 751 | }), 752 | -- The nonempty-node inserts text depending on whether the arg-node is 753 | -- empty. 754 | s("nempty", { 755 | i(1, "sample_text"), 756 | n(1, "i(1) is not empty!"), 757 | }), 758 | -- dynamic lambdas work exactly like regular lambdas, except that they 759 | -- don't return a textNode, but a dynamicNode containing one insertNode. 760 | -- This makes it easier to dynamically set preset-text for insertNodes. 761 | s("dl1", { 762 | i(1, "sample_text"), 763 | t({ ":", "" }), 764 | dl(2, l._1, 1), 765 | }), 766 | -- Obviously, it's also possible to apply transformations, just like lambdas. 767 | s("dl2", { 768 | i(1, "sample_text"), 769 | i(2, "sample_text_2"), 770 | t({ "", "" }), 771 | dl(3, l._1:gsub("\n", " linebreak ") .. l._2, { 1, 2 }), 772 | }), 773 | }, 774 | java = { 775 | -- Very long example for a java class. 776 | s("fn", { 777 | d(6, jdocsnip, { 2, 4, 5 }), 778 | t({ "", "" }), 779 | c(1, { 780 | t("public "), 781 | t("private "), 782 | }), 783 | c(2, { 784 | t("void"), 785 | t("String"), 786 | t("char"), 787 | t("int"), 788 | t("double"), 789 | t("boolean"), 790 | i(nil, ""), 791 | }), 792 | t(" "), 793 | i(3, "myFunc"), 794 | t("("), 795 | i(4), 796 | t(")"), 797 | c(5, { 798 | t(""), 799 | sn(nil, { 800 | t({ "", " throws " }), 801 | i(1), 802 | }), 803 | }), 804 | t({ " {", "\t" }), 805 | i(0), 806 | t({ "", "}" }), 807 | }), 808 | }, 809 | tex = { 810 | -- rec_ls is self-referencing. That makes this snippet 'infinite' eg. have as many 811 | -- \item as necessary by utilizing a choiceNode. 812 | s("ls", { 813 | t({ "\\begin{itemize}", "\t\\item " }), 814 | i(1), 815 | d(2, rec_ls, {}), 816 | t({ "", "\\end{itemize}" }), 817 | }), 818 | }, 819 | } 820 | 821 | -- autotriggered snippets have to be defined in a separate table, luasnip.autosnippets. 822 | ls.autosnippets = { 823 | all = { 824 | s("autotrigger", { 825 | t("autosnippet"), 826 | }), 827 | }, 828 | } 829 | 830 | -- in a lua file: search lua-, then c-, then all-snippets. 831 | ls.filetype_extend("lua", { "c" }) 832 | -- in a cpp file: search c-snippets, then all-snippets only (no cpp-snippets!!). 833 | ls.filetype_set("cpp", { "c" }) 834 | 835 | -- Beside defining your own snippets you can also load snippets from "vscode-like" packages 836 | -- that expose snippets in json files, for example . 837 | -- Mind that this will extend `ls.snippets` so you need to do it after your own snippets or you 838 | -- will need to extend the table yourself instead of setting a new one. 839 | 840 | require("luasnip.loaders.from_vscode").load({ include = { "python" } }) -- Load only python snippets 841 | 842 | -- The directories will have to be structured like eg. (include 843 | -- a similar `package.json`) 844 | require("luasnip.loaders.from_vscode").load({ paths = { "./my-snippets" } }) -- Load snippets from my-snippets folder 845 | 846 | -- You can also use lazy loading so snippets are loaded on-demand, not all at once (may interfere with lazy-loading luasnip itself). 847 | require("luasnip.loaders.from_vscode").lazy_load() -- You can pass { paths = "./my-snippets/"} as well 848 | 849 | -- You can also use snippets in snipmate format, for example . 850 | -- The usage is similar to vscode. 851 | 852 | -- One peculiarity of honza/vim-snippets is that the file with the global snippets is _.snippets, so global snippets 853 | -- are stored in `ls.snippets._`. 854 | -- We need to tell luasnip that "_" contains global snippets: 855 | ls.filetype_extend("all", { "_" }) 856 | 857 | require("luasnip.loaders.from_snipmate").load({ include = { "c" } }) -- Load only python snippets 858 | 859 | require("luasnip.loaders.from_snipmate").load({ path = { "./my-snippets" } }) -- Load snippets from my-snippets folder 860 | -- If path is not specified, luasnip will look for the `snippets` directory in rtp (for custom-snippet probably 861 | -- `~/.config/nvim/snippets`). 862 | 863 | require("luasnip.loaders.from_snipmate").lazy_load() -- Lazy loading 864 | ``` 865 | 866 | ## 配置nvim-jdtls 867 | nvim-jdtls是java开发的核心组件了,可以说上面都算是环境准备,现在终于轮到它啦。 868 | 869 | ### 核心配置 870 | 要配置 nvim-jdtls, 添加以下内容 ftplugin/java.lua 在 neovim 配置基目录 (示例. `~/.config/nvim/ftplugin/java.lua`, 详情见 `:help base-directory`)。 871 | 872 | ```lua 873 | mkdir -p ~/.config/nvim/ftplugin/ 874 | nvim ~/.config/nvim/ftplugin/java.lua 875 | ``` 876 | 编辑文件,并且我的内容如下,请根据自己的实现情况调整。 877 | 主要就是文件的路径调整。 878 | 879 | ```lua 880 | local config = { 881 | cmd = { 882 | "java", 883 | "-Declipse.application=org.eclipse.jdt.ls.core.id1", 884 | "-Dosgi.bundles.defaultStartLevel=4", 885 | "-Declipse.product=org.eclipse.jdt.ls.core.product", 886 | "-Dlog.protocol=true", 887 | "-Dlog.level=ALL", 888 | "-Xms1g", 889 | "--add-modules=ALL-SYSTEM", 890 | "--add-opens", 891 | "java.base/java.util=ALL-UNNAMED", 892 | "--add-opens", 893 | "java.base/java.lang=ALL-UNNAMED", 894 | "-jar", 895 | "/home/vnc/.local/share/nvim/lsp/jdt-language-server/plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar", 896 | "-configuration", 897 | "/home/vnc/.local/share/nvim/lsp/jdt-language-server/config_linux", 898 | "-data", 899 | "/home/vnc/.local/share/nvim/lsp/jdt-language-server/workspace/folder" 900 | }, 901 | root_dir = require("jdtls.setup").find_root({".git", "mvnw", "gradlew"}), 902 | settings = { 903 | java = {} 904 | }, 905 | init_options = { 906 | bundles = {} 907 | } 908 | } 909 | require("jdtls").start_or_attach(config) 910 | 911 | ``` 912 | 913 | > **小坑提醒:** 914 | > org.eclipse.equinox.launcher`_1.6.400.v20210924-0641`.jar这个jar包的小版本号一直在变,不要忘记调整了,我之前就因为这个版本号浪费了好久排错。 915 | 916 | 为了方便大家理解每行配置的意思,我把配置做了注释,主要源于官方文档的翻译。 917 | 当心💀,它表示你必须调整一些东西。 918 | ```lua 919 | -- 查看 `:help vim.lsp.start_client` 了解支持的 `config` 选项的概述。 920 | local config = { 921 | -- 启动语言服务器的命令 922 | -- See: https://github.com/eclipse/eclipse.jdt.ls#running-from-the-command-line 923 | cmd = { 924 | 925 | -- 💀 926 | 'java', -- 或者绝对路径 '/path/to/java11_or_newer/bin/java' 927 | -- 取决于 `java` 是否在您的 $PATH 环境变量中以及它是否指向正确的版本。 928 | 929 | '-Declipse.application=org.eclipse.jdt.ls.core.id1', 930 | '-Dosgi.bundles.defaultStartLevel=4', 931 | '-Declipse.product=org.eclipse.jdt.ls.core.product', 932 | '-Dlog.protocol=true', 933 | '-Dlog.level=ALL', 934 | '-Xms1g', 935 | '--add-modules=ALL-SYSTEM', 936 | '--add-opens', 'java.base/java.util=ALL-UNNAMED', 937 | '--add-opens', 'java.base/java.lang=ALL-UNNAMED', 938 | 939 | -- 💀 940 | --'-jar', '/path/to/jdtls_install_location/plugins/org.eclipse.equinox.launcher_VERSION_NUMBER.jar', 941 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ 942 | -- 必须指向 修改这里为 943 | -- eclipse.jdt.ls 安装路径 实际版本 944 | 945 | '-jar', '/home/vnc/.local/share/nvim/lsp/jdt-language-server/plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar', 946 | 947 | -- 💀 948 | --'-configuration', '/path/to/jdtls_install_location/config_SYSTEM', 949 | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^ 950 | -- Must point to the Change to one of `linux`, `win` or `mac` 951 | -- eclipse.jdt.ls installation Depending on your system. 952 | --这里是我们上面解压的jdt-language-server绝对路径,我这里是linux,请根据系统类型调整 953 | '-configuration', '/home/vnc/.local/share/nvim/lsp/jdt-language-server/config_linux', 954 | 955 | -- 💀 956 | -- 请参阅 README 中的“数据目录配置”部分 957 | '-data', '/home/vnc/.local/share/nvim/lsp/jdt-language-server/workspace/folder' 958 | }, 959 | 960 | -- 💀 961 | -- 这是默认设置,如果未提供,您可以将其删除。 或根据需要进行调整。 962 | -- 每个唯一的 root_dir 将启动一个专用的 LSP 服务器和客户端 963 | root_dir = require('jdtls.setup').find_root({'.git', 'mvnw', 'gradlew'}), 964 | 965 | -- 这里可以配置eclipse.jdt.ls具体设置 966 | -- See https://github.com/eclipse/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request 967 | -- 选项列表 968 | settings = { 969 | java = { 970 | } 971 | }, 972 | 973 | -- Language server `initializationOptions` 974 | -- 您需要使用 jar 文件的路径扩展 `bundles` 975 | -- 如果你想使用额外的 eclipse.jdt.ls 插件。 976 | -- 977 | -- See https://github.com/mfussenegger/nvim-jdtls#java-debug-installation 978 | -- 979 | -- 如果您不打算使用调试器或其他 eclipse.jdt.ls 插件,您可以删除它 980 | init_options = { 981 | bundles = {} 982 | }, 983 | } 984 | -- 这将启动一个新的客户端和服务器, 985 | -- 或根据 `root_dir` 附加到现有的客户端和服务器。 986 | require('jdtls').start_or_attach(config) 987 | ``` 988 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/f84364fbd293447da022a145a5d569d3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASVRLRVlf,size_20,color_FFFFFF,t_70,g_se,x_16) 989 | 990 | ### Lombok支持 991 | 使用过Spring Boot开发的工程师,对Lombok应该不陌生吧。这个小插件可以让我们的代码变的简洁。用了以后就回不去的插件。在IDEA中使用都是正常的,用vim开发显示不正常就很难受了。 992 | 如下图所示: 993 | 994 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/cb676f13a3d84dbc922329375a842c4d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASVRLRVlf,size_20,color_FFFFFF,t_70,g_se,x_16) 995 | 996 | 997 | ```bash 998 | cd /home/vnc/.local/share/nvim/lsp/jdt-language-server 999 | #下载lombok.jar 1000 | wget https://projectlombok.org/downloads/lombok.jar 1001 | ``` 1002 | 最终我们得到的路径是`/home/vnc/.local/share/nvim/lsp/jdt-language-server/lombok.jar` 1003 | 1004 | 我们在-jar参数前面加入以下几行配置: 1005 | 1006 | ```bash 1007 | "-javaagent:/home/vnc/.local/share/nvim/lsp/jdt-language-server/lombok.jar", 1008 | "-Xbootclasspath/a:/home/vnc/.local/share/nvim/lsp/jdt-language-server/lombok.jar", 1009 | ``` 1010 | 如下加粗部分 1011 | "--add-opens", 1012 | "java.base/java.util=ALL-UNNAMED", 1013 | "--add-opens", 1014 | "java.base/java.lang=ALL-UNNAMED", 1015 | **"-javaagent:/home/vnc/.local/share/nvim/lsp/jdt-language-server/lombok.jar", 1016 | "-Xbootclasspath/a:/home/vnc/.local/share/nvim/lsp/jdt-language-server/lombok.jar",** 1017 | "-jar", 1018 | "/home/vnc/.local/share/nvim/lsp/jdt-language-server/plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar", 1019 | 1020 | 1021 | 一定要在-jar前面加,不然会出错。 1022 | 参考: 1023 | [https://github.com/mfussenegger/nvim-jdtls/issues/28](https://github.com/mfussenegger/nvim-jdtls/issues/28) 1024 | 1025 | 完成以后不报错了,代码简洁。真舒服!!! 1026 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/3c009e397d724918a70759152c20e5e9.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASVRLRVlf,size_20,color_FFFFFF,t_70,g_se,x_16) 1027 | ### 我的完整配置分享 1028 | 每个人的使用习惯都不相同,我把常用的快捷键进行了映射,供大家参考。 1029 | 1030 | - `rn`变量重命名 1031 | - `f`代码格式化 1032 | - 保存自动格式化 1033 | - ``自动导入全部缺失的包 1034 | 等等。 1035 | 1036 | 我的配置文件:`nvim ~/.config/nvim/ftplugin/java.lua` 1037 | 全部内容如下,仅大家参考: 1038 | 1039 | 1040 | ```lua 1041 | local config = { 1042 | cmd = { 1043 | "java", 1044 | "-Declipse.application=org.eclipse.jdt.ls.core.id1", 1045 | "-Dosgi.bundles.defaultStartLevel=4", 1046 | "-Declipse.product=org.eclipse.jdt.ls.core.product", 1047 | "-Dlog.protocol=true", 1048 | "-Dlog.level=ALL", 1049 | "-Xms1g", 1050 | "--add-modules=ALL-SYSTEM", 1051 | "--add-opens", 1052 | "java.base/java.util=ALL-UNNAMED", 1053 | "--add-opens", 1054 | "java.base/java.lang=ALL-UNNAMED", 1055 | --增加lombok插件支持,getter setter good bye 1056 | "-javaagent:/home/vnc/.local/share/nvim/lsp/jdt-language-server/lombok.jar", 1057 | "-Xbootclasspath/a:/home/vnc/.local/share/nvim/lsp/jdt-language-server/lombok.jar", 1058 | "-jar", 1059 | "/home/vnc/.local/share/nvim/lsp/jdt-language-server/plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar", 1060 | "-configuration", 1061 | "/home/vnc/.local/share/nvim/lsp/jdt-language-server/config_linux", 1062 | "-data", 1063 | "/home/vnc/.local/share/nvim/lsp/jdt-language-server/workspace/folder" 1064 | }, 1065 | root_dir = require("jdtls.setup").find_root({".git", "mvnw", "gradlew"}), 1066 | settings = { 1067 | java = {} 1068 | }, 1069 | init_options = { 1070 | bundles = {} 1071 | } 1072 | } 1073 | require("jdtls").start_or_attach(config) 1074 | 1075 | local current_buff = vim.api.nvim_get_current_buf 1076 | -- 在语言服务器附加到当前缓冲区之后 1077 | -- 使用 on_attach 函数仅映射以下键 1078 | local java_on_attach = function(client, bufnr) 1079 | local function buf_set_keymap(...) 1080 | vim.api.nvim_buf_set_keymap(bufnr, ...) 1081 | end 1082 | local function buf_set_option(...) 1083 | vim.api.nvim_buf_set_option(bufnr, ...) 1084 | end 1085 | 1086 | --Enable completion triggered by 1087 | buf_set_option("omnifunc", "v:lua.vim.lsp.omnifunc") 1088 | -- Mappings. 1089 | local opts = {noremap = true, silent = true} 1090 | -- See `:help vim.lsp.*` for documentation on any of the below functions 1091 | buf_set_keymap("n", "gD", "lua vim.lsp.buf.declaration()", opts) 1092 | buf_set_keymap("n", "gd", "lua vim.lsp.buf.definition()", opts) 1093 | --buf_set_keymap('n', 'K', 'lua vim.lsp.buf.hover()', opts) 1094 | buf_set_keymap("n", "gi", "lua vim.lsp.buf.implementation()", opts) 1095 | --buf_set_keymap('i', '', 'lua vim.lsp.buf.signature_help()', opts) 1096 | buf_set_keymap("n", "wa", "lua vim.lsp.buf.add_workspace_folder()", opts) 1097 | buf_set_keymap("n", "wr", "lua vim.lsp.buf.remove_workspace_folder()", opts) 1098 | buf_set_keymap("n", "wl", "lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))", opts) 1099 | buf_set_keymap("n", "D", "lua vim.lsp.buf.type_definition()", opts) 1100 | --重命名 1101 | buf_set_keymap("n", "rn", "lua vim.lsp.buf.rename()", opts) 1102 | --智能提醒,比如:自动导包 已经用lspsaga里的功能替换了 1103 | buf_set_keymap("n", "ca", "lua vim.lsp.buf.code_action()", opts) 1104 | buf_set_keymap("n", "gr", "lua vim.lsp.buf.references()", opts) 1105 | buf_set_keymap("n", "e", "lua vim.lsp.diagnostic.show_line_diagnostics()", opts) 1106 | --buf_set_keymap('n', '', 'lua vim.lsp.diagnostic.goto_prev()', opts) 1107 | buf_set_keymap("n", "", "lua vim.lsp.diagnostic.goto_next()", opts) 1108 | buf_set_keymap("n", "q", "lua vim.lsp.diagnostic.set_loclist()", opts) 1109 | --代码格式化 1110 | buf_set_keymap("n", "f", "lua vim.lsp.buf.formatting()", opts) 1111 | buf_set_keymap("n", "l", "lua vim.lsp.buf.formatting()", opts) 1112 | buf_set_keymap("n", "l", "lua vim.lsp.buf.formatting()", opts) 1113 | --自动导入全部缺失的包,自动删除多余的未用到的包 1114 | buf_set_keymap("n", "", "lua require'jdtls'.organize_imports()", opts) 1115 | --引入局部变量的函数 function to introduce a local variable 1116 | buf_set_keymap("n", "crv", "lua require('jdtls').extract_variable()", opts) 1117 | buf_set_keymap("v", "crv", "lua require('jdtls').extract_variable(true)", opts) 1118 | --function to extract a constant 1119 | buf_set_keymap("n", "crc", "lua require('jdtls').extract_constant()", opts) 1120 | buf_set_keymap("v", "crc", "lua require('jdtls').extract_constant(true)", opts) 1121 | --将一段代码提取成一个额外的函数function to extract a block of code into a method 1122 | buf_set_keymap("v", "crm", "lua require('jdtls').extract_method(true)", opts) 1123 | 1124 | -- 代码保存自动格式化formatting 1125 | vim.api.nvim_command [[autocmd BufWritePre lua vim.lsp.buf.formatting_seq_sync()]] 1126 | end 1127 | 1128 | java_on_attach(nil, current_buff) 1129 | 1130 | ``` 1131 | 1132 | 1133 | # CSDN博客地址 1134 | https://blog.csdn.net/lxyoucan/article/details/123453802 1135 | --------------------------------------------------------------------------------