首页
归档
友情链接
关于
Search
1
在wsl2中安装archlinux
138 阅读
2
nvim番外之将配置的插件管理器更新为lazy
98 阅读
3
2018总结与2019规划
69 阅读
4
从零开始配置 vim(15)——状态栏配置
58 阅读
5
PDF标准详解(五)——图形状态
46 阅读
软件与环境配置
博客搭建
从0开始配置vim
Vim 从嫌弃到依赖
linux
Emacs
MySQL
Git与Github
AndroidStudio
cmake
读书笔记
编程
PDF 标准
从0自制解释器
qt
C/C++语言
Windows 编程
Python
Java
算法与数据结构
PE结构
Thinking
FIRE
菜谱
登录
Search
标签搜索
c++
c
学习笔记
windows
文本操作术
编辑器
NeoVim
Vim
win32
VimScript
emacs
linux
elisp
文本编辑器
Java
读书笔记
反汇编
OLEDB
数据库编程
数据结构
Masimaro
累计撰写
331
篇文章
累计收到
31
条评论
首页
栏目
软件与环境配置
博客搭建
从0开始配置vim
Vim 从嫌弃到依赖
linux
Emacs
MySQL
Git与Github
AndroidStudio
cmake
读书笔记
编程
PDF 标准
从0自制解释器
qt
C/C++语言
Windows 编程
Python
Java
算法与数据结构
PE结构
Thinking
FIRE
菜谱
页面
归档
友情链接
关于
搜索到
104
篇与
的结果
2022-11-18
从零开始配置vim(29)——DAP 配置
首先给大家说一声抱歉,前段时间一直在忙换工作的事,包括但不限于交接、背面试题准备面试。好在最终找到了工作,也顺利入职了。期间也有朋友在催更,在这里我对关注本系列的朋友表示感谢。多的就不说了,我们正式进入vim 的配置吧上一节通过配置 python 的调试环境,我们大概了解了配置 dap 的基本步骤。首先需要一个 dap 的客户端负责在编辑器上显示各种调试信息,并且与用户进行交互。然后需要一个服务端,与客户端通信并完成调试的实际步骤。然后需要配置两个东西, dap.adapters 用来配置如何启动调试器,dap.configurations用来配置如何将当前项目加载到调试器上。本篇我们进一步配置 dap。让它变得更好用,并且介绍编译型语言(C/C++)调试的配置。优化界面回顾一下上一篇中在演示图片里面看到的效果。默认界面在断点位置以 B 来标识,当前运行的代码以 -> 来标识。看起来不那么的直观,我们先对它进行优化,我们采用 Visual Code 的调试图标来进行标识我们采用以下代码进行配置local dap_breakpoint_color = { breakpoint = { ctermbg=0, fg='#993939', bg='#31353f', }, logpoing = { ctermbg=0, fg='#61afef', bg='#31353f', }, stopped = { ctermbg=0, fg='#98c379', bg='#31353f' }, } vim.api.nvim_set_hl(0, 'DapBreakpoint', dap_breakpoint_color.breakpoint) vim.api.nvim_set_hl(0, 'DapLogPoint', dap_breakpoint_color.logpoing) vim.api.nvim_set_hl(0, 'DapStopped', dap_breakpoint_color.stopped) local dap_breakpoint = { error = { text = "", texthl = "DapBreakpoint", linehl = "DapBreakpoint", numhl = "DapBreakpoint", }, condition = { text = 'ﳁ', texthl = 'DapBreakpoint', linehl = 'DapBreakpoint', numhl = 'DapBreakpoint', }, rejected = { text = "", texthl = "DapBreakpint", linehl = "DapBreakpoint", numhl = "DapBreakpoint", }, logpoint = { text = '', texthl = 'DapLogPoint', linehl = 'DapLogPoint', numhl = 'DapLogPoint', }, stopped = { text = '', texthl = 'DapStopped', linehl = 'DapStopped', numhl = 'DapStopped', }, } vim.fn.sign_define('DapBreakpoint', dap_breakpoint.error) vim.fn.sign_define('DapBreakpointCondition', dap_breakpoint.condition) vim.fn.sign_define('DapBreakpointRejected', dap_breakpoint.rejected) vim.fn.sign_define('DapLogPoint', dap_breakpoint.logpoint) vim.fn.sign_define('DapStopped', dap_breakpoint.stopped)上面的代码主要配置了显示的颜色和图标。最终调试的效果如下图所示然后我们需要提供一个可用的界面用来显示调试过程中的各种信息,包括变量值和调用栈。完成这个工作的是插件 nvim-dap-ui 。我们使用如下的代码进行安装use { "rcarriga/nvim-dap-ui", requires = {"mfussenegger/nvim-dap"} }这个插件里面包装了很多调试相关的窗口,例如变量监控、调用栈等等。我们可以对他进行配置,让这些窗口元素出现在我们希望它出现的位置。为了加载这个插件我们还是按照之前的惯例,为它准备一个单独的配置文件,并且加载它。local dapui = require("dapui") dapui.setup({})我们可以使用该插件中的函数 toggle() 开打开或者关闭这些调试窗口。最终的效果就像这样每次都输入这个函数来打开和关闭调试窗口比较麻烦,因此我们这里可以使用以下代码来实现自动加载和关闭local dapui = require("dapui") dapui.setup({}) local dap = require("dap") dap.listeners.after.event_initialized["dapui_config"] = function() dapui.open({}) end dap.listeners.before.event_terminated["dapui_config"] = function() dapui.close({}) end dap.listeners.before.event_exited["dapui_config"] = function() dapui.close({}) end这段代码在 dap 的事件中注册了几个回调函数,当对应的事件发生时会调用对应的函数,我们在 dap 的调试启动时打开调试窗口,在结束时关闭调试窗口最后关于界面方面的优化再来推荐一个插件——nvim-dap-virtual-text 它的作用是在调试过程中,在变量附近事实显示变量的值。我们可以在 dap-ui 的配置文件中对他进行配置require("nvim-dap-virtual-text").setup({ enabled = true, enable_commands = true, highlight_changed_variables = true, highlight_new_as_changed = false, show_stop_reason = true, commented = false, only_first_definition = true, all_references = false, filter_references_pattern = '<module', virt_text_pos = 'eol', all_frames = false, virt_lines = false, virt_text_win_col = nil })上述的配置是官方给出的,我原封不动的复制过来了。它的效果如下图所示:配置c++基础调试环境终于到了本文最重要的环节了,就是配置 c/c++ 的调试环境,上一篇我们讲解了 Python 的配置,它代表了脚本类解释型语言的调试配置,C/C++ 代表了编译型语言的调试配置。针对 C/C++ 的调试我们选用 cpptools 作为 dap 的服务端。首先通过 MasonInstall cpptools 来下载安装它,也可以通过 :Mason 命令在图形化的界面上进行安装。然后我们还是按照之前的顺序来对他进行配置,首先配置它的加载方式local dap = require("dap") dap.adapters.cppdbg = { id = "cppdbg", type = 'executable', command = "~/.local/share/nvim/mason/bin/OpenDebugAD7", }这里我们设置它以 executable 的方式启动(在客户端调试时启动)。然后指定可执行程序的路径,如果这里报找不到 OpenDebugAD7 这种错误,可以将 ~ 改为 /home/user 这样的具体目录。然后我们配置一下客户端与服务器通信相关的内容dap.configurations.cpp = { { name = "Launch file", type = "cppdbg", request = "launch", program = function() return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/", "file") end, cwd = "${workspaceFolder}", stopAtEntry = true, }, } dap.configurations.c = dap.configurations.cpp最后我们通过一个 dap.configurations.c= dap.configurations.cpp 让c++和 c使用同一个配置。因为 C/C++ 是编译运行的,在调试的时候其实调试的是它生成的可执行程序,所以这里每次在调试的时候需要手工指定要调试的可执行程序。最后别忘了在 ftplugin/cpp.lua 中加载它另外需要注意,因为可执行程序运行时是不依赖源代码的,但是调试的时候想让调试器能够准确的知道当前在源码的位置并且能够显示当前变量的值,这个时候需要在可执行程序中打包符号表,对于linux 的 C/C++ 程序来说,只需要在编译的时候给gcc/g++ 传递 -s 参数即可。我们写一个简单的 C程序来进行实验#include <stdio.h> int main (int argc, char *argv[]) { printf("hello world\n"); for (size_t i = 0; i < 10; i++) { printf("i = %ld\n", i); } return 0; }注意: 这里我们使用的调试器仍然是gdb, cpptools 只是在上层进行了一层封装。因此这里能调试的前提是安装了gdb 调试器到此我们将关于 dap 调试的部分都基本介绍完了。其实 dap 也并没有想象中那么难,目前从安装到配置使用,都有大量的插件来方便我们使用,而且官网上基本都有配置的介绍,没有特殊需求只需要将标准配置原样拷贝粘贴即可。下一篇我们将补充一些关于 dap 的其他内容,并介绍 neovim + gdb 的组合,敬请期待!
2022年11月18日
9 阅读
0 评论
0 点赞
2022-10-24
从零开始配置vim(28)——代码的编译、运行与调试
在前面几个章节,我们逐渐为 Vim 配置了语法高亮、代码的跳转和自动补全功能。现在的 Vim 已经可以作为代码编辑器来使用了。但是想将它作为日常发开的主力编辑器来用还需要很长一段路要走,其中一个就是要为它配置代码的一键编译与运行功能。这里我们仍然以 C 和 Python 为例。一个是需要编译运行的一个是直接就可以运行的,这两个语言应该能代表大多数语言的情况。自动运行C 语言的配置在之前 vim 入门的一系列教程中我们介绍过 vim 自带 make 命令的运行机制以及如何进行自定义。对于其他语言要实现这个自动编译运行的效果我们核心的操作就是在修改 make 命令。而 C/C++ 本身采用 make 命令来进行编译和运行,所以这里 C/C++ 我们直接采用 vim 自带的 :make 命令在之前 vim 入门的一系列教程中我们介绍过 vim 自带 make 命令的运行机制以及如何进行自定义。对于其他语言要实现这个自动编译运行的效果我们核心的操作就是在修改 make 命令。而 C/C++ 本身采用 make 命令来进行编译和运行,所以这里 C/C++ 我们直接采用 vim 自带的 :make 命令我们先创建一个 C 的工程。让后使用上一节的生成 hello world 的代码片段生成一个基本的程序。然后提供一个供 :make 命令使用的 Makefile 文件main.out: main.o gcc main.o -o main.out main.o: main.c gcc -c main.c clean: rm -rf *.o *.out run: ./main.out然后我们执行 make 用来编译。如果出错了,可以使用 quickfix 相关命令跳转到对应位置。我们一般的流程是 :make 进行编译,然后使用 :make run 来进行运行。把命令搞清楚了,下面就考虑如何加快这个流程,做到一键编译运行。我们的思路还是绑定快捷键。每种语言虽然定义相同的快捷键但是运行的命令不同,我们需要根据不同的语言类型绑定对应的命令。这个时候最好的办法就是在 filetype 的机制上完成绑定的操作。我们在 lua/lsp/cpp.lua 中绑定快捷键。local on_attach = function(client, bufnr) lsp_set_keymap.set_keymap(bufnr) -- 编译 vim.api.nvim_buf_set_keymap(bufnr, "n", "<F7>", "<cmd>make<CR>", {silent = true, noremap = true}) -- 编译运行 vim.api.nvim_buf_set_keymap(bufnr, "n", "<F5>", "<cmd>make run<CR>", {silent = true, noremap = true}) end到此我们关于 C/C++ 的配置就完成了。可能显的有些简单但是已经初步可用了,小伙伴可以根据自己的需求来进一步修改这个配置。使用这个配置的前提是 C/C++ 的工程中有已经定义好的 Makefile 文件Python的配置之前我们在讲解命令的模式的提到过可以使用 % 来代表当前 buffer 所对应的文件。所以 python 的配置就比较简单了。因为 Python 不需要编译,所以这里直接绑定 <F5> 来运行vim.api.nvim_buf_set_keymap(bufnr, "n", "<F5>", "<cmd>!python %<CR>", {silent = true, noremap = true})dap 配置我们经常看到有人配置 neovim 或者 vim 的时候会介绍到 dap ,那么什么是 dap 呢? dap 的全称是 Debug Adapter Protocol 从名称上看它又是一个协议。它为多种调试器提供了一层统一的适配抽象层。有点类似于前面的介绍的 lsp。只要在适配层提供接口的实现,那么在客户端,也就是代码编辑器这端可以不做任何修改的集成不同调试联想到 lsp 的配置,我们配置dap 首先需要的是有一个 dap 的客户端,用来向调试器发送各种命令,例如下断点、显示变量名等等。另外想要能够调试也需要有具体的调试器,用来接收处理这些命令。现在思路有了,我们 这里先以 Python 为例来介绍 dap 的基本配置。首先是需要一个客户端,用于通过 neovim下发各种调试命令并实时显示调试信息。截止到 0.7 版本 NeoVim 并没有在内部集成 dap 客户端的功能,需要我们单独安装相关插件来实现这部分的功能。这里我们使用的客户端是 nvim-dap 插件。我们先使用 use {'mfussenegger/nvim-dap'} 来安装它。接着我们来定义一下相关的快捷键,这里我喜欢使用 Visual Studio 的快捷键。各位小伙伴可以自行选择自己喜欢的快捷键。这里我希望在插入模式和选择中也可以使用这些快捷键,由于 vim.api.nvim_set_keymap 函数第一个参数只能有一个模式字符串,如果采用这个函数来定义快捷键,这里同样的代码我要写三次,为了简化代码,这里介绍一个新的函数 vim.keymap.set。它与 vim.api.nvim_set_keymap 函数支持的参数相同,只是它第一个表示模式的参数可以支持用字典来一次绑定到多个模式中。这样就简化了绑定快捷键的代码量。vim.keymap.set({"i", "n", "v"}, "<F5>", "<cmd>lua require'dap'.continue()<CR>", {silent = true, noremap = true, buffer = bufnr}) vim.keymap.set({"i", "n", "v"}, "<F10>", "<cmd>lua require'dap'.step_over()<CR>", {silent = true, noremap = true, buffer = bufnr}) vim.keymap.set({"i", "n", "v"}, "<F11>", "<cmd>lua require'dap'.step_into()<CR>", {silent = true, noremap = true, buffer = bufnr}) vim.keymap.set({"i", "n", "v"}, "<F12>", "<cmd>lua require'dap'.step_over()<CR>", {silent = true, noremap = true, buffer = bufnr}) vim.keymap.set({"i", "n", "v"}, "<F9>", "<cmd>lua require'dap'.toggle_breakpoint()<CR>", {silent = true, noremap = true, buffer = bufnr})这个函数是 0.7 以后的版本引进的,如果你的是0.7之前的版本,还是老老实实的多写几遍。另外我们这里绑定了 <F5> 快捷键,因此之前我们在 Python 中,绑定的直接运行的 <F5> 键的代码需要注释一下。我们想要真正实现调试,还需要配合调试器使用。前面说 dap 只是一层协议,需要客户端服务器按照这一层协议来实现相关功能,某些调试器可能自身支持这个协议,而某些可能不支持,这样就需要额外的配置来使调试器也能支持该协议。下面我们以 Python 为例先把整个调试环境搭建起来,先跑起来再说Lsp 在安装 Server 的时候有 nvim-lsp-installer 这样的插件来专门安装 LSP server 的,那么 dap 有没有类似的插件来安装 dap 调试器相关的服务呢?有!我们使用 mason 来管理 dap 的调试器。use { "williamboman/mason.nvim" }当初我推荐过 nvim-lsp-installer 插件作为下载、管理 lsp server 的工具。后来只是知道作者发布了新的管理工具,因为比较新怕出问题就没怎么关注,后来有好多小伙伴在评论区推荐,我仔细看了一下发现它已经支持 dap 服务的管理了。那还是使用它吧 ^_^。我们可以使用 Mason 打开一个带界面的 Lsp 和 DAP 的服务管理窗口,可以使用 数字键在上面进行跳转,找到想要的服务之后直接使用 i 来安装也可以使用 MasonInstall 来安装想要的服务。我们先在插件配置中删除与 nvim-lsp-installer 相关的配置,包括 packer 中对它的引用和 plugins-config 目录中的配置。下一步就是配置 dap 的客户端与 服务端的联动,这需要配置 nvim-dap 插件,根据官方的描述我们主要配置两个部分,第一个部分叫做适配器,主要配置我们加载哪个调试器,以及如何加载调试器。这一步需要提供如下的配置框架local dap = require('dap') dap.adapters.language = { }language 是具体的调试器例如 debugpy 这里的 language 就是 python了。既然与语言相关,我们自然的想到要用 ftplugin目录。为了方便管理,这里与 lsp 配置的组织形式类似,我们将所有关于 dap 的配置都放到 lua/dap目录中。并且按语言名称来命名,例如关于 python 的 dap 配置我们放到 lua/dap/python.lua 中。然后在 ftplugin/python.lua 中加载这个配置文件即。require("dap/python")然后在 python.lua 文件中写入以下配置local dap = require('dap') dap.adapters.python = { type = 'executable'; command = '/usr/bin/python3.8'; args = { '-m', 'debugpy.adapter' }; }其中各个参数的含义如下:type : 表示启动调试器的方式, executable 表示由客户端自行启动调试器; server 表示 调试器已经单独启动了,后续客户端只需要将调试请求发送到服务器即可。command: 表示启动调试器的命令args: 表示启动调试器的命令行参数由于 python 调试工具 debugpy 是一个 Python 的第三方模块,因此这里我们使用 python -m debugpy.adapter 来启动这个调试器。接着我们需要针对语言来配置如何进行调试。它的配置都放在一个名为 dap.configurations.language 的 字典中。language 代表的是当前文件的文件类型名, 所以针对 Python 来说这里需要填写的是 dap.configurations.python。它的配置如下所示:dap.configurations.python = { { type = "python"; request = "launch"; name = "launch file"; program = "${file}"; pythonPath = function () return "/usr/bin/python3.8" end }, }各参数的含义如下:name : 是一个字符串它表示当前配置的名称,你可以理解为一个idtype: 使用哪个调试器,跟我们之前配置的 dap.adapters相关request:调试的方式,支持 attach 附加到一个已有的进程或者 launch 启动一个新进程。由于在上一步我们指定由客户端来启动调试器,因此这里应该选择 launch 来启动一个新调试进程program: 需要调试的代码, ${file} 表示当前 buffer 所对应文件pythonPath: 执行该文件需要使用的 python 解析器路径这样我们在某一个打开的文件上按下 <F5> 的时候,它会通过 pythonPath 指定的解析器来执行脚本,并且会按照配置中 request 指定的方式来打开一个新的调试器进程。并且将对应的调试命令发送到调试器完成调试工作。好了,到此为止我们配置了最基本、最简单的dap 调试。现在只是有一个勉强能用的调试工具,距离好用还差的很远,下一篇里面我们首先会对 dap 功能进行增强,美化,并讨论如何针对 C/C++ 这种编译型的语言进行调试。最后感谢各位给我提意见的小伙伴,谢谢大家!
2022年10月24日
6 阅读
0 评论
0 点赞
2022-10-18
从零开始配置vim(27)——代码片段
我们之前介绍过缩写相关的内容,缩写是可以自动帮我们将缩写的单词展开成一段完整的话。但是代码本身是结构话的,仅仅使用缩写来配置是无法完成自动生成代码这个步骤的。好在我们大量的插件来进行配置。本篇我们将要来讨论如何使用相关插件来完成代码片段自动完成的功能vsnip 插件我们之前在配置自动补全的时候已经下载了 vim-vsnip这个插件。vsnip是一个非常强大的插件,它支持我们使用与 VS Code 类似的方式来扩展定义我们自己的代码片段,同时它也内置了不同语言版本的代码片段。在之前的配置中我们还加了另外一个 friendly-snippets 。它提供了丰富的已定义好的可以直接使用的代码片段,加快了我们的编码效率。在前面介绍补全的时候我们已经安装并配置了它们。它们的效果如下图所示:自定义代码片段虽说这些插件预定义了大量的代码片段。但是他们都是通用型的代码片段,总有那么些时候无法满足我们的需求。一个明显的例子就是不同的公司有不同的代码和注释的风格。这个时候就需要我们自定义了。本篇也准备将重点放在如何自定义代码片段上。如果小伙伴们已经有了在 vscode 上自定义代码片段的经历,那么请跳过本篇以节省各位的时间。入门下面我们以 C 为例来说明如何自定义代码片段来满足我们的需求。其他语言只是填入的内容不同,在定义上并没有什么大的差别。这里我采用 C 语言一个原因是静态类型的语言更方便的演示其中的各项功能,另一个原因就是我对C/C++ 比较熟悉首先我们要找到代码片段的配置文件所在位置,这个位置保存在变量 g:vsnip_snippet_dir 中。我们可以通过 :echo g:vsnip_snippets_dir 找到它。当然也有更简单的办法,我们可以执行 :VsnipOpen 来打开该语言对应的配置文件,如果该文件不存在,命令将创建一个以对应语言名称命名的 json 文件,例如这里它会创建一个 c.json 的文件这里我们先来写一个最简单的代码片段——自动生成一个 Hello World 的 C 程序"example": { "prefix": ["hello"], "body": [ "#include <stdio.h>", "int main(int argc, char* argv[])", "{", "\tprintf(\"hello world\");", "\treturn 0;", "}" ], "description": "create a hello world" }我们来试试效果,输入 hello 将自动生成一段代码现在我们来看看这些字段都代表什么含义example:它代表该代码段的名称,也可以认为它是一个id,用于标识一个代码片段prefix:用于触发该代码片段,这里也就是我们输入 hello将会触发补全,当选择对应项的时候会调用该代码片段进行补全body:补全时自动生成的代码。它可以是一个字符串或者字符数组。虽然它本身也支持 \r\n来进行换行。但是我更倾向于使用字符数组的形式,每一行是数组中的一个字符串。description :描述信息使用占位符如果我们仅仅只能生成像上述 hello world 那样给写死的代码的话,那么它也没什么太大的用处。有时候我们的代码需要生成一个模板,关键地方应该由我们自己进行填充。这个时候可以用到占位符,例如我们使用下面的来生成一个函数"ifunc": { "prefix": ["ifunc"], "body": [ "int $1()", "{", "\treturn 0;", "}" ], "description": "create a function return int" } }占位符内容可选我们现在已经可以生成函数了,但是有一个问题摆在我们的面前。函数的返回类型多种多样,如果我们每一个类型都定义一个片段,例如 返回 int 的定义一个 ifunc,返回 float 的定义一个 ffunc。显得多此一举了,代码的重复度太高了。这个时候我们我们给占位符一些预选项以供我们选择。它的语法格式如下${1|sel1, sel2, sel3,...|}前面的1代表是第一个占位符。如果是后面的占位符需要提供选项,那么就可以依次类推例如我们将上述生成函数的代码片段改为"func": { "prefix": ["func"], "body": [ "${1|int,float,double,char,short,*|} $2()", "{", "\treturn $3;", "}" ], "description": "create a function" } }占位符间跳转生成函数的代码片段中有3个占位符,其中第一个是可以选的,第二个第三个需要我们手动填写。按照习惯,我们一般使用 <Tab> 键来进行跳转,但是这个时候我们发现它没有用。vsip 有自己的命令来跳转到占位符,因此为了保持使用习惯不变,我们需要定义快捷键vim.cmd[[imap <expr> <Tab> vsnip#jumpable(1) ? '<Plug>(vsnip-jump-next)' : '<Tab>']] vim.cmd[[smap <expr> <Tab> vsnip#jumpable(1) ? '<Plug>(vsnip-jump-next)' : '<Tab>']] vim.cmd[[imap <expr> <S-Tab> vsnip#jumpable(-1) ? '<Plug>(vsnip-jump-prev)' : '<S-Tab>']] vim.cmd[[smap <expr> <S-Tab> vsnip#jumpable(-1) ? '<Plug>(vsnip-jump-prev)' : '<S-Tab>']]由于该插件是使用 vimscript 脚本写的,它还没有提供 lua 的接口,因此这里我也就使用 vimscript 的方式来定义快捷键。使用变量使用语法 $name 或者 ${name: default} 可以插入一个变量。如果未设置变量,则会插入其默认值或空字符串。当变量未知(未定义其名称)时,会将插入的变量名称转换为占位符。这里的变量一般是环境变量或者是 vim 自带的一些变量。我们直接拿来用但是在不同的环境下得到的结果是不一样的。我们可以使用变量来丰富一些信息。例如我们使用下面的代码片段来生成注释"doc-header": { "prefix": ["doc-header"], "body": [ "/**", "* @file ${TM_FILENAME}", "* @bref ${1}", "* @details ${2}", "* @date ${CURRENT_YEAR}/${CURRENT_MONTH}/${CURRENT_DATE}", "* @commit history:", "* \t v${3}: ${4}", "*/${0}" ], "description": "create a doc header" }这样我们就生成了一个 .cpp 文件开头的注释了。其中用到了 TM_FILENAME 来表示文件名,使用 CURRENT_YEAR CURRENT_MONTH CURRENT_DATE 来分别获取到年月日。它的效果如下:这里的 ${0} 一般用来截断 Tab键的跳转,也就说遇到 ${0} 之后 <Tab> 键就不再起到跳转到下一个占位符位置这个作用了。具体有哪些变量可以使用,可以参考 visual studio code 官方给出的文档本篇主要谈论了该如何定义自己的代码片段。如果想要更完整的内容可以参考 Visual Studio Code 官方的文档。我们也可以从Visual Studio Code 相关代码片段中 Copy 部分来进行使用。
2022年10月18日
8 阅读
0 评论
0 点赞
2022-10-14
从零开始配置vim(26)——LSP UI 美化
之前我们通过几个实例演示如何配置其他语言的lsp服务,相信各位小伙伴碰到其他的编程语言也能熟练的配置它对应的lsp服务。本篇讲作为一个补充,我们来优化一下LSP 相关的显示配置 UI原始的 lsp 显示有点素,我们使用插件对它进行一些美化,这里使用插件 lspsaga.nvim 。使用如下的代码进行安装use{"glepnir/lspsaga.nvim"}然后我们新建一个 plugin-config/lspsaga.lua 对它进行配置local saga = require('lspsaga') saga.init_lsp_saga()该插件对 NeoVim 原生 LSP 显示做了一些更改,并提供了一些方便的命令来实现LSP 相关的功能。我们将它对应的功能绑定到快捷键上替换原有的 LSP 对应的快捷键lsp_keybinds.set_keymap = function (bufnr) print("set lsp keymap") -- 跳转到声明 vim.api.nvim_buf_set_keymap(bufnr, "n", "gd", "<cmd>Lspsaga peek_definition<CR>", {silent = true, noremap = true}) -- 跳转到定义 vim.api.nvim_buf_set_keymap(bufnr, "n", "gD", "<cmd>lua vim.lsp.buf.definition()<CR>", {silent = true, noremap = true}) -- 显示注释文档 vim.api.nvim_buf_set_keymap(bufnr, "n", "gh", "<cmd>Lspsaga lsp_finder<CR>", {silent = true, noremap = true}) -- 跳转到实现 vim.api.nvim_buf_set_keymap(bufnr, "n", "gi", "<cmd>lua vim.lsp.buf.implementation()<CR>", {silent = true, noremap = true}) -- 跳转到引用位置 vim.api.nvim_buf_set_keymap(bufnr, "n", "gr", "<cmd>Lspsaga rename<CR>", {silent = true, noremap = true}) -- 以浮窗形式显示错误 vim.api.nvim_buf_set_keymap(bufnr, "n", "go", "<cmd>lua vim.diagnostic.open_float()<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "gp", "<cmd>lua vim.diagnostic.goto_prev()<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "gn", "<cmd>lua vim.diagnostic.goto_next()<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>cd", "<cmd>Lspsaga show_cursor_diagnostics<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>cd", "<cmd>Lspsaga show_line_diagnostics<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>ca", "<cmd>Lspsaga code_action<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "v", "<leader>ca", "<cmd>Lspsaga code_action<CR>", {silent = true, noremap = true}) end我们可以对比一下原生的 lsp 界面和 lspsaga 界面,我这里使用 gh 这个命令进行对比我们可以看到它能显示更丰富的信息,而且我们可以根据显示快速跳转到对应位置。对于我们查阅代码也是一个大的提升而且它还提供 code action 相关的功能。例如上面的截图中它在出现错误的一行代码的行号前以小灯泡的形式进行标记,体验类似与 VS Code。我们将光标放在对应位置,使用绑定的快捷键 <leader>ca 。它会显示出对应的修复方案当然它的功能还不止于此,各位小伙伴可以去对应的官网上阅读相关文档,针对自己的需求进行进一步的配置显示符号表我们可以使用插件 symbols-outline 在窗口右侧显示当前 buffer 中出现的类、方法等符号,方便我们快速跳转到想去的地方。该插件仅支持 NeoVim7,0 以上的版本。使用下面的代码进行安装use {'simrat39/symbols-outline.nvim'}还是额外的给它创建一个文件保存它的配置require("symbols-outline").setup()配置完成之后我们可以使用 :SymbolsOutline 命令来控制窗口的打开和关闭,为了方便我们可以绑定到一个快捷上配置完成之后我们可以使用 :SymbolsOutline 命令来控制窗口的打开和关闭,为了方便我们可以绑定到一个快捷上vim.api.nvim_set_keymap("n", "<leader>so", "<cmd>SymbolsOutline<CR>", {silent = true, noremap = true})最终的效果如下图所示本章我们对之前的LSP 配置进行了一定程度的优化,优化了使用的体验。截止到当前文章我们已经完成了代码的高亮、跳转、语法分析、代码补全、错误提示以及修复建议的相关配置。关于LSP的相关配置已经结束了,下一篇我们将要讨论如何使用代码片段进一步减少我们编码时输入的字符数。请各位敬请期待
2022年10月14日
8 阅读
0 评论
0 点赞
2022-10-09
从零开始配置vim(25)——关于 c++ python 的配置
从9月份到国庆这段时间,因为得了女儿,于是回老家帮忙料理家事以及陪伴老婆和女儿。一时之间无暇顾及该系列教程的更新。等我回来的时候发现很多小伙伴私信我催更。在这里向支持本人这一拙劣教程的各位小伙伴表示真诚的感谢。言归正传,让我们开始吧之前我们根据lua语言配置了基于lsp的代码高亮、自动跳转、自动补全等等功能,那个时候我们安装了很多插件,像 nvim-lspconfig、nvim-lsp-installernvim-cmp等等,每个插件都在干嘛,虽然我们配置好了 lua相关的内容,但是可能仍然有小伙伴有疑问,碰到其他语言该如何配置,是不是要重新下载对应的插件呢?为了解答这些问题,这篇文章我们将要来根据 c++和 python的日常习惯来进行配置,给大家演示一下在上述内容都配置完成之后面对其他语言我们该如何进行处理安装配置 c++ 相关的lsp服务关于c++ 的服务,我们根据 nvim-lsp-installer 官方给出的表格中显示它可以使用 ccls 和 clang,这里我们以 ccls 作为示例进行讲解。首先通过命令安装 :LspInstall ccls接着我们新建一个 ftplugin/c.lua 和 ftplugin/cpp.lua 来配置 c/c++ 。不过他们两个采用相同的配置,我们暂时将一份配置复制两遍require("lsp/cpp")他们的作用只有一个,那就是加载 lsp/cpp 这个文件,我们将他们的配置放到一个文件中然后我们再在 lua/lsp/cpp.lua 文件中加入以下内容用于启动 lsp服务端local lspconfig = require('lspconfig') lspconfig.ccls.setup { init_options = { cache = { directory = ".ccls-cache"; }; } }我们进入一个 .c/.cpp 文件发现已经加载了 ccls 了。如果没有加载可以使用 :LspStart 命令手工加载或者使用 LspInfo 查看是否有问题现在我们已经可以看到lsp服务给出的提示了修改之前的配置我们在第22篇文章中给出了基于 lsp 的 lua 的配置,主要是使用 lsp 服务端的配置和对应的跳转之类的快捷键配置,我们将它放到了 lsp/lua.lua 目录下了。但是一想想我们使用 c++、Python 或者其他什么语言的时候,这些快捷键应该是不会修改的,这个时候自然就想到了要重用快捷键了。所以来配置之前的第一件事就是想办法重用这些快捷键。这个时候我们想到的办法就是将之前定义的快捷键封装成函数,然后在 on_attach 的回调函数中调用该函数。我们将那些快捷键定义放到 lua/keybindings.lua 中local lsp_keybinds = {} lsp_keybinds.set_keymap = function (bufnr) -- 跳转到声明 vim.api.nvim_buf_set_keymap(bufnr, "n", "gd", "<cmd>lua vim.lsp.buf.declaration()<CR>", {silent = true, noremap = true}) -- 跳转到定义 vim.api.nvim_buf_set_keymap(bufnr, "n", "gD", "<cmd>lua vim.lsp.buf.definition()<CR>", {silent = true, noremap = true}) -- 显示注释文档 vim.api.nvim_buf_set_keymap(bufnr, "n", "gh", "<cmd>lua vim.lsp.buf.hover()<CR>", {silent = true, noremap = true}) -- 跳转到实现 vim.api.nvim_buf_set_keymap(bufnr, "n", "gi", "<cmd>lua vim.lsp.buf.implementation()<CR>", {silent = true, noremap = true}) -- 跳转到引用位置 vim.api.nvim_buf_set_keymap(bufnr, "n", "gr", "<cmd>lua vim.lsp.buf.references()<CR>", {silent = true, noremap = true}) -- 以浮窗形式显示错误 vim.api.nvim_buf_set_keymap(bufnr, "n", "go", "<cmd>lua vim.diagnostic.open_float()<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "gp", "<cmd>lua vim.diagnostic.goto_prev()<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "gn", "<cmd>lua vim.diagnostic.goto_next()<CR>", {silent = true, noremap = true}) end return lsp_keybinds我们将它打包到lsp_keybinds 模块中作为 keybinds.lua 文件的导出模块。然后在 lsp/lua.lua文件的 on_attach 函数中调用这个函数完成快捷键的配置工作local lsp_set_keymap = require("keybindings") local on_attach = function(_, bufnr) lsp_set_keymap.set_keymap(bufnr) end现在我们就完成了 c/c++ 相关的配置尝试一下绑定的那些快捷键,发现它可以正常进行跳转是不是很简单了,我们没有安装任何的插件,只是安装了 c++ lsp 的服务端。剩下的配置依然延续之前的就好了python 相关配置我们再来以 python 的配置作为例子来讲lsp的配置。python 的服务端我们采用 pyright 。还有一个方法可以安装 lsp 服务。我们可以在命令模式中输入 :LspInstallInfo 查看当前已安装的 lsp 服务。下面会列出一堆的未安装的 lsp 服务。这个时候我们可以将光标移动到某个服务上,按下 i 来安装该服务。这里我提前已经安装好了,所以它显示在 Installed Server 中,没有安装它应该显示在下方的Available Server 列表中。安装完成之后我们还是按照惯例,在 ftplugin 目录下建立一个 python.lua 文件并且在该文件中加载 lua/lsp/python.lua 文件我们在 lua/lsp/python.lua 文件中加入下面的代码local lsp_set_keymap = require("keybindings") local util = require 'lspconfig/util' require('lspconfig').pyright.setup{ on_attach = function(_, bufnr) lsp_set_keymap.set_keymap(bufnr) end, cmd = { "pyright-langserver", "--stdio" }, filetypes = { "python" }, settings = { python = { analysis = { autoSearchPaths = true, diagnosticMode = "workspace", useLibraryCodeForTypes = true, typeCheckingMode = "off" }, }, }, root_dir = function(fname) local root_files = { 'pyproject.toml', 'setup.py', 'setup.cfg', 'requirements.txt', 'Pipfile', 'pyrightconfig.json', } return util.root_pattern(unpack(root_files))(fname) or util.find_git_ancestor(fname) or util.path.dirname(fname) end, }前面的配置与 c++ 的配置类似。主要设置快捷键,这里需要注意的是 root_dir 这里的配置,想要在对应缓冲中启动相应的 lsp 服务,需要buffer处在对应语言的项目中,root_dir 规定在 buffer 所在目录中存在这些文件或者目录时将该目录作为对应项目文件如果我们不加该配置,可能会导致lsp 服务启动失败。如果失败的话我们使用 :LspInfo 来查看失败原因这个根目录设置是可以作用于它下面所有子目录的。这里我根据 python 中常用文件给出了一个列表,各位小伙伴可以根据自己的需求自行添加另外需要注意的一个问题时,pyright 依赖于 node 的 work_threads 模块,该模块从12版本以后才被支持,如果小伙伴的 node 版本低于该版本需要考虑升级 node根据这两个例子,相信各位已经熟悉了该如何配置不同语言的 lsp 服务了。这里面没有什么深奥的代码,也没有什么个性化到只有自己才会用的配置,希望能起到抛砖引玉的作用,后面再碰到什么其他语言小伙伴们应该可以很容易的添加它的lsp服务了。
2022年10月09日
6 阅读
0 评论
1 点赞
2022-09-08
从零开始配置vim(24)——自动补全
neovim 自带的代码补全的效果并不好,而且它分为好多类,如果需要人为的去判断使用路径补全、使用当前buffer中的单词补全、亦或者使用include 来进行补全,那样使用起来就很不方便了。针对代码的补全我们可以基于 lsp的配置使用插件来完成,这里我推荐使用 nvim-cmp插件安装我们使用下面的代码进行安装-- nvim-cmp use {'hrsh7th/cmp-nvim-lsp'} use {'hrsh7th/cmp-buffer'} use {'hrsh7th/cmp-path'} use {'hrsh7th/cmp-cmdline'} use {'hrsh7th/nvim-cmp'} -- vsnip use {'hrsh7th/cmp-vsnip'} use {'hrsh7th/vim-vsnip'} use {'rafamadriz/friendly-snippets'} -- lspkind use {'onsails/lspkind-nvim'}看着好像要安装好多插件,但是真正重要的就只有 nvim-cmp 剩下的以 cmp- 开头的都是在指定补全的来源,就像 neovim 自带补全功能可以来源于当前buffer、路径、引用一样,该插件的补全项也来源于各个地方。例如 cmp-nvim-lsp来源于语言的 lsp 服务端、cmp-buffer来源于当前 buffer 、cmd-path来源于路径配置我们还是按照原来的惯例进行配置,新增一个 plugin-config/cmp.lua 的配置文件,并且加载它require('plugin-config/cmp')在该配置文件中我们加入如下内容local cmp = require("cmp") local lspkind = require("lspkind") cmp.setup({ -- 设置代码片段引擎,用于根据代码片段补全 snippet = { expand = function(args) vim.fn["vsnip#anonymous"](args.body) end, }, window = { }, mapping = { -- 选择上一个 ['<C-p>'] = cmp.mapping.select_prev_item(), -- 选择下一个 ['<C-n>'] = cmp.mapping.select_next_item(), -- 出现补全 ['<A-.>'] = cmp.mapping(cmp.mapping.complete(), {'i', 'c'}), -- 取消补全 ['<A-,>'] = cmp.mapping({ i = cmp.mapping.abort(), c = cmp.mapping.close(), }), -- 确认使用某个补全项 ['<CR>'] = cmp.mapping.confirm({ select = true, behavior = cmp.ConfirmBehavior.Replace }), -- 向上翻页 ['<C-u>'] = cmp.mapping(cmp.mapping.scroll_docs(-4), {'i', 'c'}), -- 向下翻页 ['<C-d>'] = cmp.mapping(cmp.mapping.scroll_docs(4), {'i', 'c'}), }, -- 补全来源 sources = cmp.config.sources({ {name = 'nvim_lsp'}, {name = 'vsnip'}, {name = 'buffer'}, {name = 'path'} }), --根据文件类型来选择补全来源 cmp.setup.filetype('gitcommit', { sources = cmp.config.sources({ {name = 'buffer'} }) }), -- 命令模式下输入 `/` 启用补全 cmp.setup.cmdline('/', { mapping = cmp.mapping.preset.cmdline(), sources = { { name = 'buffer' } } }), -- 命令模式下输入 `:` 启用补全 cmp.setup.cmdline(':', { mapping = cmp.mapping.preset.cmdline(), sources = cmp.config.sources({ { name = 'path' } }, { { name = 'cmdline' } }) }), -- 设置补全显示的格式 formatting = { format = lspkind.cmp_format({ with_text = true, maxwidth = 50, before = function(entry, vim_item) vim_item.menu = "[" .. string.upper(entry.source.name) .. "]" return vim_item end }), }, })配置之后我们就可以使用自动补全相关功能了本篇的内容到此结束了,后面我们再次以 c/c++以及 python为例来演示如何进行 lsp相关的配置。帮助大家巩固一下这方面的内容。
2022年09月08日
5 阅读
0 评论
0 点赞
2022-09-07
从零开始配置vim(23)——lsp基础配置
上一章,我们初步认识了lsp,并且对 nvim-treesitter插件进行了配置,为编辑器提供了代码着色、自动格式化以及增量选中功能。算是初步体验了 lsp的相关功能。从这篇开始我们通过lsp的功能,进一步提升代码编辑、查阅等功能的体验neovim lsp早期想通过 neovim 使用 lsp 得通过一个额外的插件 nvim-coc 。它使用 nodejs实现,而且提供 lua 的接口。但是在 neovim 中混用不同编程语言有时候会出现莫名其妙的问题,比如我之前遇到的补全失效,但是过一段时间它自己又好了。好在 neovim 社区听从了程序员们对于 lsp 的呼唤,它内置了lsp 的客户端,并且为了方便配置服务端,它提供了一个名为 nvim-lspconfig 的插件。在现在的 neovim 版本下配置不同语言的 lsp已经很方便了,根据官方的文档,我们只需要4步即可安装 nvim-lspconfig 插件安装对应语言 lsp 的服务端针对 xx语言,添加语言的配置 require('lspconfig').xx.setup{…}检查 lsp 的服务端在该缓冲区是否正常运行 print(vim.inspect(vim.lsp.buf_get_clients()))安装 nvim-lspconfig好了,我们按照官方的提示,我们先来配置 lua 相关的内容,以便后期在写配置时可以有更好的编程体验。use {'neovim/nvim-lspconfig'}我们可以去微软的官方网站查看各个语言的服务端信息 https://microsoft.github.io/language-server-protocol/implementors/servers/安装对应服务端针对 lua 语言我们选中的是 sumneko/lua-language-server 这个服务端。根据 wiki 页面的安装方式,我们可以使用命令行自行安装。但是现在有了更方便的方式了我们使用 nvim-lsp-installer 插件进行安装。use { "williamboman/nvim-lsp-installer", "neovim/nvim-lspconfig", }我们还是一样,在plugin-config 目录下创建一个配置文件用来配置 nvim-lsp-installer 插件require("nvim-lsp-installer").setup {}我们可以使用 LspInstallInfo 命令来查看当前lsp服务的安装情况。我们使用 :LspInstall --sync [server] 来安装对应的服务端。其中 --sync 代表我们希望以同步的方式安装,也就是安装时会卡主 neovim 主体。使用下面的命令来安装 lua 的服务端:LspInstall --sync sumneko_lua我们可以在 这个页面 查看 nvim-lsp-installer 插件支持的各个语言对应的服务端针对lua进行配置安装完成之后,我们来配置 lua 相关的内容。不知道还记不记得我们之前介绍 文件类型的时候说过不同文件类型的配置都在 ~/.config/nvim/ftplugin 里面。我们在这个目录里面定义一个 lua.lua 的文件,写入以下内容-- 是否将 tab 替换为 space vim.bo.expandtab = true vim.bo.shiftwidth = 4 vim.bo.tabstop = 4 vim.bo.softtabstop = 4 -- 取消自动注释,当前行是注释时,按下回车键会默认添加一行注释,这里取消这一行为 vim.opt_local.formatoptions = vim.opt_local.formatoptions - {"r", "c", "o"}我们之前分析过 neovim 是如何实现文件类型检测的。在那篇文章中我们说它定义了自动命令,当检测到对应文件类型的时候会调用 ~/.config/nvim/ftplugin 目录中对应以文件类型命名的目录或者 lua 文件。使用这种方式有两个好处,第一个就是我们不用手动使用 require 来加载了,第二个好处就是可以根据文件类型动态的选择调用或者不调用,而且这个工作由 neovim 自动完成,不需要我们进行干预基于这些好处,我们在 ftplugin/lua.lua 中保存 lua 相关的配置。另外再在 ~/.config/nvim/lua 目录创建一个 lsp目录,专门用来保存语言的配置。然后再在 ftplugin/lua.lua 中加载它require('lsp/lua')我们在这个文件中添加 Lua 相关的lsp配置-- 定义快捷键 -- 根据官方的提示,这里我们使用 on_attach 表示当前缓冲加载服务端完成之后调用 local on_attach = function(client, bufnr) -- 跳转到声明 vim.api.nvim_buf_set_keymap(bufnr, "n", "gd", "<cmd>lua vim.lsp.buf.declaration()<CR>", {silent = true, noremap = true}) -- 跳转到定义 vim.api.nvim_buf_set_keymap(bufnr, "n", "gD", "<cmd>lua vim.lsp.buf.definition()<CR>", {silent = true, noremap = true}) -- 显示注释文档 vim.api.nvim_buf_set_keymap(bufnr, "n", "gh", "<cmd>lua vim.lsp.buf.hover()<CR>", {silent = true, noremap = true}) -- 跳转到实现 vim.api.nvim_buf_set_keymap(bufnr, "n", "gi", "<cmd>lua vim.lsp.buf.implementation()<CR>", {silent = true, noremap = true}) -- 跳转到引用位置 vim.api.nvim_buf_set_keymap(bufnr, "n", "gr", "<cmd>lua vim.lsp.buf.references()<CR>", {silent = true, noremap = true}) -- 以浮窗形式显示错误 vim.api.nvim_buf_set_keymap(bufnr, "n", "go", "<cmd>lua vim.diagnostic.open_float()<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "gp", "<cmd>lua vim.diagnostic.goto_prev()<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "gn", "<cmd>lua vim.diagnostic.goto_next()<CR>", {silent = true, noremap = true}) end require'lspconfig'.sumneko_lua.setup { settings = { Lua = { runtime = { -- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim) version = 'LuaJIT', }, diagnostics = { -- Get the language server to recognize the `vim` global globals = {'vim'}, }, workspace = { -- Make the server aware of Neovim runtime files library = vim.api.nvim_get_runtime_file("", true), }, -- Do not send telemetry data containing a randomized but unique identifier telemetry = { enable = false, }, }, }, on_attach = on_attach, }大部分的配置都是根据 sumneko_lua 官方的文档抄过来的,这里我们需要着重强调一下 on_attach 这里,根据 nvim-config 的描述,当 lsp 服务程序加载完成之后会调用我们在 on_attach 出指定的回调函数,在函数内部我们使用 vim.api.nvim_buf_set_keymap 仅仅针对当前加载了lsp 服务的缓冲区进行,而对于其他普通文件我们不需要用到这些快捷键。看到这里不知道小伙伴是否有点头晕了,这次我们安装了好多内容,也创建了不少配置文件,下面来回顾一下安装 nvim-lspconfig 来用于lsp的配置安装 nvim-lsp-installer 来安装不同语言的 lsp 服务端安装 sumneko_lua 作为 lua 语言的 lsp 服务端创建了一个 ~/.config/nvim/ftplugin 作为加载 lua 语言配置的入口创建了一个 ~/.config/nvim/lsp/lua.lua 保存lua 语言相关的配置本篇只讲了如何基于 neovim 官方给出的那4步来配置一个语言的 lsp 服务端,但是还有如何自动补全没有谈到,下节我们将开始讲解如何使用自动补全。敬请期待!
2022年09月07日
14 阅读
0 评论
1 点赞
2022-09-06
从零开始配置vim(22)——lsp简介与treesitter 配置
截止到上一篇文章,我们配置了neovim的很多内容了。具备了一些编辑器的常用功能了,而且可以胜任日常的文档编辑工作了。但是想作为一个可靠的代码编辑器还缺少重要的一环,即代码语法部分的支持。在过去的vim配置中,我们基于 you-complete-me这个插件配置。但是对于不懂c语言甚至vim的小白来说简直是灾难。各种兼容问题、报错频出,而且效果也远不如 visual studio code等编辑器。也有可能是我那个时候比较菜,当初针对 python配置的补全效果很一般只能补全内置函数,自己定义的类和方法无法补全,而且跳转时好时坏。后来我抛弃了vim很长一段时间。好在微软提供了lsp这个大杀器,让vim、emacs这类编辑器的代码编辑体验提升了很大一截。而且配置还相对简单。lsp 简介过去的编辑器包揽了诸如代码高亮、语法分析、跳转等功能,这样就导致了所有编辑器都有自己专有的一套显示、跳转等方案。而第三方编辑器想要达到完全相同的效果几乎是不可能的。但是微软提出的 lsp(language server protocol) 确改变了这一格局。lsp最重要的就是将语法分析、跳转、自动补全功能这些语言的核心功能和最终呈现效果分开。即server端主要提供了语法分析、补全、跳转的核心功能,而在客户端要做的就是调用服务端提供的这些功能来展示以及通过快捷键或者其他用户接口以便用户使用。而且语言服务器是以进程的方式单独运行,并不会影响客户端的运行。它们之间通过本地网络的形式进行信息交换用户在工具中打开一个的文件, 该工具通知语言服务器文档打开 ('textDocument/didOpen') 。 从现在起,有关文档内容不再位于文件系统上,而是保存在编辑器开辟的一块内容中用户进行编辑:该工具通知服务器文档更改 ('textDocument/didChange') ,程序语义信息由语言服务器更新。 发生这种情况时,语言服务器会分析此信息,并通知工具 ('textDocument/publishDiagnostics') 检测到的错误和警告,并且还可能返回一些可能的用于补全的内容用户对编辑器中的符号执行“转到定义”:该工具发送具有两个参数的“textDocument/definition”请求: (1) 文档 URI, (2) 从服务器启动 Go to Definition 请求的文本位置。 服务器使用文档 URI 和符号定义在文档中的位置进行响应。客户端接到返回后,根据服务器标记的位置进行跳转用户关闭文档 (文件) :工具发送“textDocument/didClose”通知,通知语言服务器文档现在不再处于内存中,并且将当前内容保存到文件系统中。treesitter 配置我们简单介绍过 neovim-treesitter 这个插件,它可以用来做代码高亮。它采用 lsp 协议实现,比起单纯使用正则表达式来说,它具有更好的渲染效果。那么我们体验 lsp 效果的第一步就来配置它吧。treesitter 想要工作,需要根据语言下载配套语言对应的语法解析模块,我们可以使用 :TSInstallInfo 来查看当前我们安装了哪些解析模块。我们发现并没有安装任何的模块,基于当前配置文件的工程,我们先来体验一下 lua 的效果。我们使用 :TSInstall <language>的命令可以下载指定语言的模块。这里我们使用 :TSInstall lua 来下载lua模块。后续我们可以使用 :TSUpdate lua 来更新该模块。跟 packer 类似的 :TSUpdate 即可以用来下载也可以用来更新。也就是一条命令就搞定了安装完成之后我们可以使用 :TSBufToggle highlight 来使用 treesitter 进行高亮如果我们每次都需要手工调用命令来进行高亮的话,就太不智能了。我们可以在配置文件中配置它自动加载语法高亮。require('nvim-treesitter.configs').setup({ -- 支持的语言 ensure_installed = {"html", "css", "vim", "lua", "javascript", "typescript", "c", "cpp", "python"}, -- 启用代码高亮 highlight = { enable = true, additional_vim_regex_highlighting = false }, --启用增量选择 incremental_selection = { enable = true, keymaps = { init_selection = '<CR>', node_incremental = '<CR>', node_decremental = '<BS>', scope_incremental = '<TAB>' } }, -- 启用基于 Treesitter 的代码格式化(=) indent = { enable = true }, }) -- 开启代码折叠 vim.wo.foldmethod = 'expr' vim.wo.foldexpr = 'nvim_treesitter#foldexpr()' -- 默认不折叠 vim.wo.foldlevel = 99我们来一条条的解释这些配置ensure_installed 表示需要支持哪些语言,如果里面设置了某些语言,那么在启动之后它会自动调用 :TSUpdate 来下载和更新对应语言的 server 部分。等它下载完了对应的语言模块之后,我们发现它已经很好的完成了代码着色的功能。增量选择可以一次选择一块的代码,依次扩大或者缩小所选择的语言块,我们使用回车来开始和扩大增量选择,使用退格键来减少增量选择代码块。各位小伙伴可以根据自己的习惯来定义快捷键另外我们可以使用 = 来格式化代码。为了方便我们定义自动命令,每当执行 :w 写入前前自动格式化代码local auto_indent = vim.api.nvim_create_augroup("AUTO_INDENT", {clear = true}) vim.api.nvim_create_autocmd({"BufWritePost"}, { pattern = "*", group = auto_indent, command = 'normal! gg=G``' })这里因为提前使用了 gg改变了光标位置,在格式化之后使用 \`\` 来回到上次跳转之前的位置。最后我们可以使用 zc 和 zo 来折叠和展开代码。不过我自己很少用,需要查看文件中的符号例如函数、变量等我可以使用其他插件来解决,如果要成块的跳转代码我比较喜欢使用 %。好了,到此为止我们先体验了一下基于 lsp 实现的 treesitter 的功能,后面将展开讲述 lsp 对一些语言的支持,敬请期待
2022年09月06日
16 阅读
0 评论
1 点赞
2022-09-05
从零开始配置vim(21)——会话管理
很多代码编辑器都有这么一个功能,重新进入编辑器之后能恢复上次打开的所有文件,窗口布局,有的甚至是上次设置的一些配置。那么vim是否也可以实现这样的功能呢?答案是肯定的。使用vim自带的会话管理和 viminfo 可以实现恢复上次打开文件和布局以及重置上次的配置它们二者分别保存了不同的内容,一般会将它们联合起来使用。会话一般保留上次打开的文件、窗口布局、以及一些全局设置viminfo 保留的是历史命令行记录、搜索替换模式记录、标签、非空寄存器的值、缓冲区列表、全局信息等等看起来很好像显的很杂一样,但是我觉得这些都不是需要记忆的,我们只需要知道使用使用 viminfo 和session可以恢复所有我们关心的内容即可,无需分辨哪些内容保存在哪个位置。我们使用自动命令来保存和加载它们,你甚至可能会忘记它们的存在。会话我们还是按照惯例,先来试试vim原生的内容我们可以使用 :mksession [file] 来保存一个当前的会话。再重新进入vim 的时候可以使用 :source session-file 来加载一个会话文件。在实际生成会话文件时,我们可以省略这个文件名,这个时候vim会自动生成一个采用默认文件名的会话文件。vim会默认在当前工作目录下生成一个 Session.vim的文件,我们打开这个文件发现它本质上就是一个vim的脚本,跟我们写的配置文件是一回事。各位小伙伴可以打开看看,试着读一下它里面具体在干嘛。我们来试着做一下这个实验,就以当前正在使用的 vim 配置这个工程。我们随机打开几个文件,然后执行 :mksession ,然后退出关闭vim之后,我们发现在项目的根目录也就是 ~/.config/nvim 下,生成一个了一个 Session.vim 。有兴趣的小伙伴可以打开来看看里面的内容。它其实就是一个vim脚本,跟我们写的配置是一回事接着我们试着在终端输入 nvim 不带任何参数,直接打开我们的欢迎界面,在该界面中输入 :source Session.vim 。执行完成之后我们发现,它帮助我们将显示还原成了上次退出之前的样子默认session要保存 哪些内容是由一个名为 sessionoption的变量决定的。不知道小伙伴还记不记得如何来查看一个变量现在的值?(:set sessionoption?) 。使用set在对应变量后面加上一个 ? 表示查询当前变量值。我们看到它目前保存这些内容 sessionoptions=blank,buffers,curdir,folds,help,tabpages,winsize。他们分别代表着空窗口、所有缓冲区、当前目录、折叠、帮助窗口、标签页、窗口大小。viminfo 文件vim每次在退出时会自动在用户的家目录保存一个名为 .viminfo 的文件,每次退出后都会发生覆盖行为。有时我们在编辑项目之后又重新打开vim编辑了另外一些内容,这个时候就有可能发生覆盖行为,把我们项目相关的内容给覆盖掉了,那这个时候该怎么办呢?好在vim提供了 :wviminfo命令来指定写入到哪个viminfo文件中。我们可以使用 :rviminfo来读入指定的viminfo文件。小伙伴可能有一个问题。既然Session.vim 是一个vim脚本,为什么不把所有设置都写在配置文件里面呢?我们可以这么做,但是提供一个额外的session功能可以根据项目来灵活的调整配置,我们将所有项目的通用配置保存在我们自己的配置文件里面,将某些项目特有的配置放到session中,由vim自己维护,这样vim能更好的适应不同的使用场合。viminfo 保存的是命令行,搜索记录这些,而且每次打开都会自动加载,所以就不像session 这样能很明显的看出效果。很明显的一个特征就是,即使我们这次打开仅仅只用了 :q 这一个命令,但是我们通过 q: 仍然可以看到之前几次打开时执行过的命令简单的配置为了防止小伙伴做无用功,我这里事先声明一下,针对会话管理我们是有专门的插件的。这里所做的配置在后面都会被我们弃用的,小伙伴可以不往自己的配置里面加了。但是想试试也可以,有些事只有自己亲手做了才能更好的理解首先我们希望它在退出的时候能自动保存Session和viminfo文件到对应的项目根目录下。在启动时能重新加载这些文件。自然要自动进行这些内容,我们想到的就是自动命令了。我们先来定义一个函数来保存这两个文件function save_session() local curdir = vim.api.nvim_eval([[getcwd()]]) local session_file = curdir .. "/Session.vim" vim.cmd([[mksession ]] .. session_file) local viminfo = curdir .. "/.viminfo" end在该函数中,我们使用 vim的 getcwd 命令来获取当前vim所在的工作目录。工作目录的概念我们在之前介绍vim的时候已经介绍过了,忘记的小伙伴请自行折回去复习。然后我们拼接一个字符串,让vim保存session和viminfo文件到当前工作目录中我们再来添加一个函数用来加载 session 内容。function load_session() local curdir = vim.api.nvim_eval([[getcwd()]]) local session_file = curdir .. "/Session.vim" local viminfo = curdir .. "/.viminfo" file, err = io.open(session_file, "r") if err == nil then file:close() vim.cmd([[source ]] .. session_file) end file, err = io.open(viminfo, "r") if err == nil then file:close() vim.cmd([[rviminfo ]] .. viminfo) end end与保存函数不同的是,我这里加了一个判断文件是否存在的代码,因为我们无法得知用户会在哪个目录下打开vim,无法事先知道该目录是否有这些文件,所以这里先判断一下,如果没有文件就不进行任何操作。最后我们添加一个自动命令组来自动保存和加载 session 文件和 viminfo文件。local auto_save_session = vim.api.nvim_create_augroup("AUTO_SAVE_SESSION", {clear = true}) vim.api.nvim_create_autocmd({"ExitPre"}, { pattern = "*", group = auto_save_session, callback = save_session }) vim.api.nvim_create_autocmd({"VimEnter"}, { pattern = "*", group = auto_save_session, callback = load_session })我们在终端直接输入 nvim 发现它自动恢复了上次打开的所有缓冲区。我们来试一下效果,先删除上一次生成的 Session.vim 文件,接着退出然后再次打开 vim ,我们发现它恢复到了打开之前的样子到此我们已经有了一个简单的自动加载session 和上次配置的功能。如果希望有更多的定制可以考虑使用插件,但是如果只是简单的需要此功能的,可以就这么配置或者自己改改配置达到自己的需求。auto-session 插件上面我们已经定义了可以自动加载和保存上次会话的功能。有的小伙伴可能觉得这个功能太简单了,我希望能给我提供更方便、更加灵活的使用方式,而且我们上面的配置有一个很大的问题,我输入 nvim init.lua 已经指定了要打开的文件,它仍然会打开之前保存的 session ,这里我再介绍一个能够管理 session 的插件—— auto-session ,它能有效的避免上面的问题。我们使用如下的语句来安装 auto-sessionuse {'rmagatti/auto-session'}然后按照惯例,新建文件并且添加配置require("auto-session").setup({ log_level = "error", -- 打开这些目录里面的内容时,不加载会话 auto_session_suppress_dirs = {"~/", "~/Projects", "~/Downloads", "/", "/ect", "/usr"} auto_session_enable_last_session = false, -- 保存session文件到 ~/.local/share/nvim/sessions目录 auto_session_root_dir = vim.fn.stdpath('data').."/sessions/", auto_session_enabled = true, auto_save_enabled = nil, auto_restore_enabled = nil, auto_session_use_git_branch = nil, -- the configs below are lua only bypass_session_save_file_types = nil })我们可以在 lualine 的配置文件中 sections 一节中添加 lualine_c = {require('auto-session-library').current_session_name} 来显示当前打开的会话名称最后根据官方的提示,我们可以给sessionoption 赋值,多保存两个内容vim.o.sessionoptions="blank,buffers,curdir,folds,help,tabpages,winsize,winpos,terminal"这样我们如果打开了终端,下次进入时还可以恢复终端窗口最后我们再来补充一下之前 dashboard 的功能,之前定义的有一个恢复上次会话的功能{icon = " ", desc = 'Recently lastest session ', shortcut = "Leader s l", action = "RestoreSession"}我们可以在 custom_center 中添加这么一行,具体的位置可以看我提交到github中的仓库到此为止,关于session的配置就结束了。
2022年09月05日
7 阅读
0 评论
1 点赞
2022-09-01
从零开始配置vim(20)——模糊查询
在讲解vim的基础功能的时候,介绍过了vim的各种查询技巧,在同一个文件中进行搜索的话,那些技巧很有用。在多个文件中我们介绍了使用vim自带的 :grep命令进行搜索,使用quickfix 列表进行跳转,但是比起其他代码编辑器来说,总归有那么一些繁琐光是输入参数都已经很麻烦了,我想要像其他代码编辑器那样直接输入内容它就能基于工程来进行匹配。本篇文章我们将要来对它进行优化,达到这一目的。本次我们要介绍的是神级插件 telescope,一般只要介绍 neovim 配置的文章98%以上的都会推荐这个插件作为文件搜索和文本搜索的插件。我们自然也不能免俗,这里我也要介绍它,如果不介绍就显得有点不太专业了。虽然我也介绍,但是这个系列毕竟是从0开始配置vim,我会从安装到配置进行描述,希望能比其他的教程要详细一点。但是最详细的仍然是它的官方文档。安装我们使用下面的代码进行安装use { 'nvim-telescope/telescope.nvim', tag = '0.1.0', requires = { {'nvim-lua/plenary.nvim'} } }为了更好的使用体验可以安装一个 nvim-treesitter 插件,它主要用于代码高亮,它采用语法分析的形式对代码进行高亮,相比于使用正则表达式来说效果更好,后面会详细的介绍如何进行配置,加上它之后 telescope 插件将会更加强大。这个时候我们的安装代码应该改为use { 'nvim-telescope/telescope.nvim', tag = '0.1.0', requires = { {'nvim-lua/plenary.nvim'} , { 'nvim-treesitter/nvim-treesitter', run = function() require('nvim-treesitter.install').update({ with_sync = true }) end, }} }这里加上一个 run 的配置主要是安装 nvim-treesitter 插件之后,第一次会报错,后面是正常的,这句话是为了消除第一次加载时报错安装完成之后,我们就可以在命令中输入 :Telescope find_files 来按照名称搜索文件它常用的命令有如下几个find_files : 查找文件live_grep: 使用正则表达式来进行内容的搜索,它可以跨文件搜索buffers:查看当前打开的缓冲区,并且可以预览缓冲区的内容grep_string: 以当前光标所在单词进行搜索oldfile: 打开历史文件列表marks: 打开书签表jumplist: 打开跳转列表如果我们希望能够使用 live_grep 和 grep_string 的功能需要提前在系统上安装 ripgrep。例如在ubuntu 上可以使用下列命令安装sudo apt-get install ripgrep配置我们先对最常用的功能进行快捷键的映射vim.api.nvim_set_keymap("n", "<leader>ff", "<Cmd>Telescope find_files<CR>", {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "<leader>gg", "<Cmd>Telescope live_grep<CR>", {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "<leader>fm", "<Cmd>Telescope marks<CR>", {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "<leader>fj", "<Cmd>Telescope jumplist<CR>", {noremap = true, silent = true})不知道各位小伙伴是否还记得,我们在配置启动界面的时候留下了几个功能没有添加,现在我们有了这个插件之后就可以添加一部分了在 dashboard 的配置中,我们可以补充相关功能对应的命令如下db.custom_center = { {icon = " ", desc = 'Recently lastest session ', shortcut = "Leader s l", action = ""}, {icon = " ", desc = "Recently opened files ", shortcut = "Leader f h", action = "Telescope oldfiles"}, {icon = " ", desc = "Find File ", shortcut = "leader f f", action = "Telescope find_files"}, {icon = " ", desc = "File Browser ", shortcut = "leader f b", action = "Telescope file_browser" }, {icon = " ", desc = "Find Word ", shortcut = "leader g g", action = "Telescope live_grep"}, {icon = " ", desc = "Open Personal dotfiles ", shortcut = "leader e e", action = "edit $MYVIMRC"} }要使用 Telescope file_browser 的功能我们需要额外安装一个插件。它扩展了 Telescope 插件的功能use { "nvim-telescope/telescope-file-browser.nvim" }并且我们需要在 telescope 配置中加载这个扩展require('telescope').load_extension "file_browser"这样我们就完成了几乎所有的功能了,还差一个加载上次会话的功能,我们等到讨论会话的时候再来补上到现在我们的配置基本就结束了,我们目前仅仅只使用了它极为有限的功能。根据官方的文档,它有大量的导出函数用于各种功能,但是现在我们并不打算做太多的定制化开发,仅仅拿来用即可,所以目前的配置我认为已经够用了。各位小伙伴也可以根据自己的需求查阅官方文档进行额外的配置
2022年09月01日
10 阅读
0 评论
1 点赞
1
...
4
5
6
...
11