普通视图

发现新文章,点击刷新页面。
昨天以前ISLAND

用vscode开发 ios/macos App

2025年12月8日 09:24

如何脱离 xcode 开发 iOS 是很多人在探索的方案。毕竟 xcode 编辑体验实在是太差了。

这里说的是脱离 xcode,而不是脱离 macOS。

SweetPad

这是今天要安利的工具 SweetPad。SweetPad 是 vscode 上的一个插件,插件可以实现在 vscode 中进行自动补全、调试、编译和运行、格式化、测试等功能。常见的开发场景中基本可以脱离 xcode 来使用。

LazyVim 使用

2025年2月15日 13:45

在使用 Neovim 之前,我也花费了大量的时间进行配置自己的 Neovim 体系,无论是插件,快捷键还是 UI,都花费了大量的心思。 但是维护一套自己的配置说实话很费时间,例如插件的更新,版本升级等。

LazyVim 是一款已经 集成了大量插件的 Neovim 软件。 基本做到了开箱即用,方便快捷,大量的默认配置减少了上手时间,唯一的缺点可能就是快捷键不是自己熟悉的那套,需要修改或者适应。

安装

LazyVim 的安装其实很简单,首先打开官方的启动模板 LazyVim/starter。 点击右上角 Use this template ,将模板 fork 成为自己的仓库。 接下来就是备份本地原有的配置,将自己的仓库 clone 下来。

简单来说就是一个命令进行备份 另外一个命令进行 clone。 这部分可以直接参考官方网站对于安装的介绍文档。

安装完成之后,启动就是一个已经有了大量基础配置的 neovim 了,基本做到了开箱即用。

完成 LazyVim 的配置,记得看一下是否系统安装了 fzf

配置说明

在 LazyVim 的项目中,配置可以分成两大块:基础配置(config) 和 插件(plugins)。

config 配置

config 配置主要是一些基础的配置,文件夹内区分了四个文件, 分别是基础配置(options.lua),快捷键配置(keymaps.lua),自动化命令(autocmds.lua)和 lazy 初始化配置。

plugins 配置

默认 LazyVim 已经集成了很多插件,有的已经默认开启,有的需要手动开启。如果需要覆盖原有的插件配置或者安装新的插件, 可以在该文件夹下直接填写相关配置,最后在外层的 init.lua 中添加即可。

快捷键

LazyVim 已经集成了大量的插件,并且默认了很多快捷键。默认的 Leader 为 空格。可以针对自己的习惯修改快捷键,也可以直接按下 Leader 来查看快捷键。

Keymaps

默认的快捷键我认为是比较合理,而且使用几次基本也会记住,这里我除了修改了一下 blink 候选提示的上下选择之后,并没有做其他修改。 默认的快捷键也可以在官方文档中找到 keymaps

LazyExtras

LazyVim 一些默认的插件已经启用,但是除了默认插件,还有一些其他插件可以根据具体的情况按需使用。 通过 :LazyExtras 命令可以查看其他扩展插件。如果有自己使用的,需要的可以直接通过 x 进行启用。 需要注意的是启用后需要重新启动一些 neovim。

自动命令

LazyVim 中自带了一些配置,例如 tab 为 2,在一些缩进比较多语言中,tab 为 2是比较友好的,例如 lua,但是对于一些项目,尤其是很团队合作的项目, tab 改成2 就会让代码一团乱。

options.lua 中新增一些 tab 相关的配置。

local opt = vim.opt
opt.expandtab = true
opt.tabstop = 4
opt.shiftwidth = 4
opt.softtabstop = 4

同时为了让 lua 还保持 tab 为2的缩进,在 autocmds.lua 中新增自动化命令。

vim.api.nvim_create_autocmd("FileType", {
  pattern = { "lua" },
  callback = function()
    vim.opt_local.expandtab = true
    vim.opt_local.tabstop = 2
    vim.opt_local.shiftwidth = 2
    vim.opt_local.softtabstop = 2
  end,
})

还有一个就是中文拼写问题,默认的 spell 一直检查,尤其是在进行 markdown 的时候,大量的波浪线导致编辑的感官太差,可以新增命令来解决。

vim.api.nvim_create_autocmd("FileType", {
  pattern = { "markdown", "txt" },
  callback = function()
    vim.opt_local.spell = false
  end,
})

vscode 使用 LazyVim

VSCode Neovim

LazyVim 对 vscode 支持比较好,并且 vscode-neovim 也推荐使用 lazyvim。通过这个插件,可以让我们在 Vscode 中加载一些 Neovim 的插件,做到一个配置两处使用。

首先需要在 LazyExtras 中打开 Vscode 。 然后在 Vscode 中安装 Vscode Neovim 插件。

在 LazyVim 中可以通过 vim.g.vscode 来判断当前的运行环境。

首先是对快捷键进行配置,比如常用的重命名、格式化等。

local map = vim.keymap.set
if vim.g.vscode then
  map("n", "<leader>cf", "<Cmd>lua require('vscode').call('editor.action.formatDocument')<CR>", { desc = "Format" })
  map("v", "<leader>cf", "<Cmd>lua require('vscode').call('editor.action.formatSelection')<CR>", { desc = "Format" })
  map("n", "<leader>cr", "<Cmd>lua require('vscode').call('editor.action.rename')<CR>", { desc = "Rename" })
end

Golang 开发

针对 Golang 开发,首先就是语言的相关配置:

  • LSP:通过 Mason 安装 gopls
  • Testing: 通过 LazyExtras 进行安装 test.core
  • DAP: 通过 Mason 安装 dlv

安装完成就可以进行编辑,运行,测试和调试。

可以通过 <Leader>tr 进行测试运行。

Go Testing

通过 <Leader>db 进行打断点。 通过 <Leader>dc 进行 Debug 运行。

Go Debug

neovim入门指南(三):LSP配置(上)

2023年9月4日 18:10

🧩 什么是 LSP

对于一个编辑器来说,如果要完成例如自动补全,查找相关定义等功能是需要进行大量的开发的。不同的编辑器为了不同的语言都需要进行开发,而 LSP 的存在就是将这个过程检化。LSP 的全称为 Language Server Protocol,定义了编辑器和语言服务之间使用的协议。只要相关语言支持 LSP,那么编辑器只要符合相关要求实现即可完成例如自动补全等功能,而且不同的编辑器使用的体验是一致的。

目前支持 LSP 的编辑器有很多,例如大名鼎鼎的 Vscode。当然 vim 8 以后版本和 neovim 也都支持,具体支持的编辑器/IDE 列表可以看 LSP 的官方网站,同时支持 LSP 的语言也可以找到 支持语言

neovim 已经是支持 LSP 了,具体可以在相关的配置文档看到,该文档详细的描述了如何配置一个 LSP。相对来说,配置过程比较繁琐,所以官方又提供了另一个库 nvim-lspconfig。接下来我们就通过这个插件来配置 neovim 的 lsp。

nvim-lspconfig

与安装其他插件是一样的,只需要我们在 plugins_config.lua 中添加相关配置即可,这里不进行赘述了。安装完成后其实进行配置就可以启用 LSP 了,就是这么简单。例如支持 rust 的 LSP,只需要进行简单的配置。

lspconfig.rust_analyzer.setup {
  settings = {
    ['rust-analyzer'] = {},
  },
}

但是,nvim 只是 lsp 的客户端,那么就存在 lsp 的服务端。上面配置的 rust_analyzer 就是 rust 语言的服务端,就需要我们进行服务端的安装。rust_analyzer 的服务端地址是 rust-lang/rust-analyzer,需要将服务端下载并且安装好,这样每次编写rust的时候就会享受 lsp 的服务加成了。

但是这样做有几个问题,当然也不能算问题,只是不太方便。

  1. 对于多语言使用者来说,需要手动安装多个 lsp 服务端,目前 lsp 的服务端应该是没有统一的下载安装地址,需要手动寻找;
  2. 每次服务端进行更新,都需要重新下载安装;
  3. 新换设备之后,无法开箱即用,需要重复上述的方式重新开始一次。

面对上面的不方便,你可能已经想到很多解决方法,例如写个脚本进行一键安装和更新常用的 lsp 服务端。这样基本解决了上面说的所有问题。正如你想的那样,今天的第二位主角 williamboman/mason.nvim

🗃️ mason.nvim

mason 是一个可以方便的管理 LSP 服务端,DAP 服务端,Linter 和 格式化工具的插件。安装它之后,上面所说的问题将不是问题。

为了让 mason 和 nvim-lspconfig 更好的配合,这里还需要安装另一个插件 williamboman/mason-lspconfig.nvim

同样的安装这里不多赘述,主要是进行相关的配置。这里为了区别其他的插件,我们在 lua 目录下建立新的文件夹 lsp,用来专门存放 lsp 的配置。

配置 mason

首先还是加载我们的插件。在 lsp 文件夹中新建 mason.lua 文件,在文件中新增下面的配置。配置主要是在加载插件。

-- mason.lua
local mason_status, mason = pcall(require, "mason")
if not mason_status then
 vim.notify("没有找到 mason")
 return
end

local nlsp_status, nvim_lsp = pcall(require, "lspconfig")
if not nlsp_status then
 vim.notify("没有找到 lspconfig")
 return
end

local mlsp_status, mason_lspconfig = pcall(require, "mason-lspconfig")
if not mlsp_status then
 vim.notify("没有找到 mason-lspconfig")
 return
end


mason.setup()
mason_lspconfig.setup({})

📤 安装 lsp 服务端

配置完成后,重新启动 nvim,此时就可以采用 mason 进行 LSP 的服务端进行管理了。只需要按下 :Mason 即可。你将会看到如下的界面。

Mason 界面

通过界面上的帮助可以看到如何使用,通过数字可以选择不同的服务端项目,2 为 LSP , 3 为 DSP 等。今天只是使用 LSP,可以直接按 2,选择到 LSP 界面,进行 LSP 安装。仍旧是通过 jk 进行滑动。第一个安装的 lsp 服务端为 lua 语言的服务端:lua-language-server。这个是 lua 语言的语言服务,有 lsp 之后,我们之后无论是配置 nvim 还是编写 lua 都会有 lsp 服务的加持。按下 i 进行安装。

稍等片刻,安装完成。接下来就是配置,让 nvim 知道我们的 lsp 已经安装,在合适的时候进行启动。

-- mason.lua
nvim_lsp.lua_ls.setup({
 on_init = function(client)
  local path = client.workspace_folders[1].name
  if not vim.loop.fs_stat(path .. "/.luarc.json") and not vim.loop.fs_stat(path .. "/.luarc.jsonc") then
   client.config.settings = vim.tbl_deep_extend("force", client.config.settings, {
    Lua = {
     runtime = {
      version = "LuaJIT",
     },
     workspace = {
      checkThirdParty = false,
      library = {
       vim.env.VIMRUNTIME,
      },
     },
    },
   })

   client.notify("workspace/didChangeConfiguration", { settings = client.config.settings })
  end
  return true
 end,
})

这样 lua 的 lsp 就配置成功了,当我们编写 lua 脚本的时候,如果发生错误就会有相关提醒。当然这只是 lsp 最基础的功能,例如代码跳转,代码补全等需要我们进行配置。

发生错误时的提示

基本所有的 lsp 的配置都可以在 server_configurations.md 中找到,当然 lua_ls 也不例外,上面的配置就是直接从文档中复制的 😄。

当前的 LSP 配置已经支持代码跳转,code action 等功能。例如查看当前变量或者函数的文档,可以使用这个命令 :lua vim.lsp.buf.hover()

相关的命令还有其他

功能 命令
文档显示 :lua vim.lsp.buf.hover()
查看定义 :lua vim.lsp.buf.definition()
重命名 :lua vim.lsp.buf.rename()
查询实现 :lua vim.lsp.buf.implementation()
查询引用 :lua vim.lsp.buf.refreences()
查询声明 :lua vim.lsp.buf.declaration()
格式化 :lua vim.lsp.buf.format()
Code action :lua vim.lsp.buf.code_action()

对于这些基础功能来说,每次需要的时候都在命令模式下敲一堆,速度的确是很慢的。所以,可以将上述的命令定义为快捷键,这样每次只需要进行快捷键进行完成上述功能。

⌨️ 快捷键绑定

打开我们之前设置快捷键的配置文件 keybinding.lua,新增上述功能的配置。

-- keybinding.lua
-- lsp 快捷键设置
pluginKeys.lspKeybinding = function(mapbuf)
 -- rename
 mapbuf("n", "<leader>r", ":lua vim.lsp.buf.rename<CR>", opt)
 -- code action
 mapbuf("n", "<leader>ca", ":lua vim.lsp.buf.code_action()<CR>", opt)
 -- go to definition
 mapbuf("n", "gd", ":lua vim.lsp.buf.definition()<CR>", opt)
 -- show hover
 mapbuf("n", "gh", ":lua vim.lsp.buf.hover()<CR>", opt)
 -- format
 mapbuf("n", "<leader>=", ":lua vim.lsp.buf.format { async = true }<CR>", opt)
end

完成快捷键的配置,那么就可以将快捷键绑定到刚刚配置的 lsp 服务端了。

-- mason.lua
function LspKeybind(client, bufnr)
 local function buf_set_keymap(...)
  vim.api.nvim_buf_set_keymap(bufnr, ...)
 end
 -- 绑定快捷键
 require("keybinding").lspKeybinding(buf_set_keymap)
end

接下来可以完成快捷键的绑定。

-- mason.lua
nvim_lsp.lua_ls.setup({
 on_attach = LspKeybind,
 on_init = function(client)
    -- 省略其他配置
 end,
})

这样就完成了 lua 的 lsp 的配置,在编写 lua 的时候就可以使用文档查看,code Action 等功能。

目前这些 lsp 的服务都要手动下载,对于一些日常使用的服务,我们可以通过配置,在第一次加载配置的时候,当机器上没有相关的服务的时候,自动下载,这样来说,基本实现了我们上述提出的问题。

-- mason.lua
mason_lspconfig.setup({
 automatic_installation = true,
 ensure_installed = { "lua_ls", "rust_analyzer" },
})

这样配置,如果我们本地没有安装 lua 和 rust 的 lsp,会自动进行下载安装。

自动补全

直到目前,在 lsp 的加持下,其实编辑体验已经变得非常棒了,而且开发速率也会大幅提升,虽然 lsp 是支持自动补全功能的,但是上面其实一直没有提及。主要是单单靠 neovim 的功能还不够强大,需要插件的配置。

hrsh7th/nvim-cmp 是一个采用 lua 编写的补全引擎,通过 cmp 及 cmp 的相关插件,会将 neovim 的自动补全达到一个新的高度。

这里除了 nvim-cmp,再推荐几个 cmp 的相关插件。更多的相关插件可以在 wiki 中找到

配置补全

同样不赘述安装。在 lsp 文件夹中新建 cmp.lua 文件夹。

-- cmp.lua
local status, cmp = pcall(require, "cmp")
if not status then
    vim.notify("找不到 cmp")
    return
end

剩下了就可以将之前的补全源进行配置。这里贴出我的补全源。

sources = cmp.config.sources({
	{ name = "codeium" }, -- 需要安装 codeium.nvim
	{ name = "nvim_lsp" },
	-- For vsnip users.
	{ name = "vsnip" },
}, {
	{ name = "buffer" },
	{ name = "path" },
}),

此时当我们进行输入的时候就可以看到自动补全的提示了。对于自动补全的提示,上下选择并且上屏,我们可以设置快捷键,来满足我们的使用习惯。

和之前设置快捷键一样,在 keybindings.lua 中添加相关配置。

-- keybindings.lua
pluginKeys.cmp = function(cmp)
 return {
  -- 出现补全
  ["<A-.>"] = cmp.mapping(cmp.mapping.complete(), { "i", "c" }),
  -- 取消
  ["<A-,>"] = cmp.mapping({
   i = cmp.mapping.abort(),
   c = cmp.mapping.close(),
  }),
  -- 上一个
  ["<C-k>"] = cmp.mapping.select_prev_item(),
  -- 下一个
  ["<C-j>"] = cmp.mapping.select_next_item(),
  -- 确认
  ["<CR>"] = cmp.mapping.confirm({
   select = true,
   behavior = cmp.ConfirmBehavior.Replace,
  }),
 }
end

最后在 cmp.lua 中使用这些快捷键即可。

cmp.setup({
    -- 省略其他配置
    mapping = require("keybindings").cmp(cmp),
})

这样便可以完成自动补全的配置了。

小结

目前为止,已经完成 nvim 的 lsp 的相关配置,并且添加了自动补全。篇幅限制,剩下如何美化 lsp 提示,美化自动补全等我们下篇再说。

我的 neovim 相关配置,可以提供大家进行参考 youngxhui/nvim

Nvim配置

2021年10月26日 16:23
阅读建议
当前文章过于粗糙,建议阅读 neovim 使用指南系列文章

这已经是我第 N + 1 次尝试 vim 了。这是之前的 基础教程

个人认为学习 vim 的曲线是比较陡峭的,尤其是刚刚开始的时候,各种指令让人根本记不住。就连熟悉的 Ctrl c/v 大法都不能用了。

这里我安装的是 neovim

命令

后来开始慢慢的尝试,发现要学会 vim 的各种命令,首先要明白各种命令代表着是什么意思。

命令 操作 解释
a 在光标尾部进行编辑 append
i 在光标首部进行编辑 insert
o 在光标下一行进行编辑 -
y 复制 yank
p 粘贴 pates
u 撤销 undo
ctrl+r 反撤销 redu

命令实在太多了,需要慢慢记忆和使用。

插件

任何一款强大的编辑器都离不开插件系统的支持,有插件的支持可以使得任何文本编辑器逐渐媲美IDE。

在vim中安装插件要麻烦一点,毕竟原有的 vim 本身并没有插件系统,需要其他程序来实现。

neovim 在 windows 中的配置文件在 ~/AppData/Local/nvim 中的 init.vim (如果没有该文件自己新建一个即可)。

首先安装插件系统,对于 vim 中的插件系统是很多的。例如 Vundle、NeoBundle、VimPlug等。

我使用的是 vimPlug 。首先要安装 VimPlug 。对于 Vimplug 在windows上的安装只需要一行命令。

bash

iwr -useb https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim |`
    ni "$(@($env:XDG_DATA_HOME, $env:LOCALAPPDATA)[$null -eq $env:XDG_DATA_HOME])/nvim-data/site/autoload/plug.vim" -Force

安装完成需要将下面配置添加到 init.vim 中的插件系统是很多的。例如

vim

call plug#begin('~/AppData/Local/nvim/plugged') 


call plug#end()

插件将在这个块中进行配置。

新增插件后只需要保存后,通过命令 :PlugInstall 命令进行安装即可。

Golang 环境

对 Golang 的支持

vim-go 是一个很好的插件,安装即可完成对 golang 的支持。其中第一次使用的时候需要安装一些配置。通过命令 :GoInstallBinaries 安装必要的包。等待安装完成后就有了基本的对 Golang 的支持了,比如运行代码等。通过 :GoRun 就可以完成代码的运行,但是目前还有问题,下方的输出会看不到多余的输出代码,这个问题还要继续研究研究。

vim-go 还有很多功能,日后逐渐熟悉了再介绍。

自动补全

自从微软提出了 LSP 服务,自动补全变得比较统一。通过安装 coc-nvim 来进行自动补全。我这里主要是 Golang 的自动补全。

需要 nodejs 环境进行自动补全。 Coc 是 vim 的插件,同时 Coc 又有自己的插件(套娃组合)

通过 :CocInstall 进行安装服务。

在完成 Golang 的 LSP 服务后,就可以完成自动补全了。

效果如下所示

有自动补全的提示和文档提示。

Go程序设计语言读书笔记

2021年5月21日 20:39

重新系统学习 Golang。之前学习Golang都是用什么学什么,不系统,不全面,很多知识点一知半解。这次通过阅读 《Go程序设计语言》这本书来系统的学习一下。

命令行参数

这是我第一次知道 Golang 其实是可以从启动是直接输入参数的,之前看到很多库都是使用 flag,也让我一度认为启动时传入参数必须使用 flag,比较这也好理解,谁让 Golang 的 main 函数和 java 或者 c 不太一样呢?

1
2
3
4
5
// java
public static void main(String[] args)

// c
int main(int argc, char *argv[])

Golang 直接 func main() ,想传入参数也不知道如何进行。

原来 Golang 是有个 os.Args 这个方法,Args的底层数据结构是一个字符串切片 var Args []string,这样就方便的获取到从命令行输入的参数。但是这里的 Args[0] 并不是传入的第一个元素,而是该程序的名字。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println(os.Args[0]) 
    // 输出结果 C:\Users\YOUNGX~1\AppData\Local\Temp\go-build2749142914\b001\exe\echo1.exe
}

所以要获得该程序从命令行传入的参数应该为 Args[1:],这样可以获取全部传入的值。

指针

首先指针未赋值初始化为 nil

其他零值

官方对其他类型做出了规定 The Zero value

类型 初始值
bool false
numeric 0
string ""
pointers,functions,interfaces nil
slices,channels,map nil

同时指针是可比较的,当且仅当指向同一个变量或者都为 nil 的时候才相同

new()

new() 函数通过传入一个类型,得到该类型的指针。同时每次执行new() 返回的指针值是不一样的。但是有一些例外:两个变量的类型不携带任何信息且是零值,它们有相同的地址,例如struct{}1.16 版本实现好像已经更改。

基本数据类型

Go 的基本数据类型分为四类:基本类型,聚合类型,引用类型和接口类型。

  • 基本类型:数字,字符串,布尔
  • 聚合类型:数组,结构体
  • 引用类型:指针,slice,map,function,函数,通道

字符串

字符串是不可变类型。这一点和 Java 是一致的,但是为什么呢?不可变意味着两个字符串可以安全地公用同一段底层内存,使得复制任何长度字符串的开销都低廉。

字符串可以与字节 slice 相互转换。

1
2
3
s := "abc"
b := []byte(s)
s2  := string(b)

字符串操作包

之前听过这样一句话,一个语言的成熟与否要看他对字符串的方法多不多。Golang 提供了以下包对字符串进行处理。bytesstringsstrconvunicode

数组

对于数组来说, [3]int[4]int 是两个不同的数组类型。数组的长度必须是常量表达式(要你有何用?)。

Golang 的数组还挺有意思的,例如下面这样

1
symbol := [...]int{99:-1}

表示长度为 100 的数组,其中前99个都是 0,最后一个为 -1。

Golang 在数组和其他类型上都是值传递。需要引用传递使用指针。

1
2
3
func zero(ptr *[32]byte) {
	*prt = [32]byte{}
}

Golang 中的数组和 PHP 的数组还有点相似,可以作为k-v使用,但是 k 只能是数。arr := []string{0: "1", 2: "2"} ,这个数组遍历后的结果为如下:

1
2
3
0 1
1 
2 2

会多出一个新的索引 1

Slice

Slice 的底层为一个可变数组。

1
2
3
4
5
6
// src/runtime/slice.go
type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

slice 无法用 == 比较,与 nil 可以,标准库中只有 bytes.Equal 比较两个 byte 的slice。 其他的就要自己实现了。(难道是因为没有泛型,作者要写多个实现?)

Struct

struct 结构体嵌套,而且匿名嵌套

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
type Point struct {
	x int
	y int
}

type Circle struct {
	Point
	Radius int
}

type Wheel struct {
	Circle
	Spokes int
}

这里的 PointCircle 可以采用这种匿名方式。而调用可以直接调用,像下面这种情况,第3行和第4行为相同的调用。

1
2
3
4
5
func main() {
	var w Wheel
	w.x = 5
	w.Circle.Point.x = 5
}

但是不允许在同一个结构体中定义两个相同类型的匿名成员。

匿名函数

匿名函数可以作为函数的参数和返回值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func squares() func() int {
	var x int
	return func() int {
		x++
		return x * x
	}
}

func main() {
	f := squares()
	fmt.Println(f())
	fmt.Println(f())
	fmt.Println(f())
	fmt.Println(f())
}

defer

之前只用来作为资源关闭的操作,没想到还有 其他sao 操作。

defer后面可以为一个函数,通过函数调用来实现更多功能。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func bigSlowOperation() {
	defer trace("bigSlowOperation")()
	time.Sleep(10 * time.Second)
}

func trace(msg string) func() {
	start := time.Now()
	log.Printf("enter %s", msg)
	return func() {
		log.Printf("exit %s (%s)", msg, time.Since(start))
	}
}

使用 defer 也有很多需要注意的地方。例如在循环中使用 defer。这个是无效的,并不能及时的回收资源,最好是将循环体和 defer 封装为一个函数,每次调用函数后会执行 defer。

宕机与恢复

宕机会引起程序异常退出。宕机代表的程序执行的终止,但是 Golang 提供了宕机恢复函数 recoverrecover 会终止当前的宕机状态,并且返回宕机值。

方法与函数

Golang 中对方法和函数有定义,emmmm🦤,方法是特殊的函数。

1
2
3
4
5
6
7
8
9
// 函数
func name(parameter-list) (result-list) {
    body
}

// 方法
func (t Type)name(parameter-list) (result-list) {
    body
}

neovim入门指南(三):LSP配置(上)

2023年9月4日 18:10

🧩 什么是 LSP

对于一个编辑器来说,如果要完成例如自动补全,查找相关定义等功能是需要进行大量的开发的。不同的编辑器为了不同的语言都需要进行开发,而 LSP 的存在就是将这个过程检化。LSP 的全称为 Language Server Protocol,定义了编辑器和语言服务之间使用的协议。只要相关语言支持 LSP,那么编辑器只要符合相关要求实现即可完成例如自动补全等功能,而且不同的编辑器使用的体验是一致的。

目前支持 LSP 的编辑器有很多,例如大名鼎鼎的 Vscode。当然 vim 8 以后版本和 neovim 也都支持,具体支持的编辑器/IDE 列表可以看 LSP 的官方网站,同时支持 LSP 的语言也可以找到 支持语言

neovim 已经是支持 LSP 了,具体可以在相关的配置文档看到,该文档详细的描述了如何配置一个 LSP。相对来说,配置过程比较繁琐,所以官方又提供了另一个库 nvim-lspconfig。接下来我们就通过这个插件来配置 neovim 的 lsp。

nvim-lspconfig

与安装其他插件是一样的,只需要我们在 plugins_config.lua 中添加相关配置即可,这里不进行赘述了。安装完成后其实进行配置就可以启用 LSP 了,就是这么简单。例如支持 rust 的 LSP,只需要进行简单的配置。

1
2
3
4
5
lspconfig.rust_analyzer.setup {
  settings = {
    ['rust-analyzer'] = {},
  },
}

但是,nvim 只是 lsp 的客户端,那么就存在 lsp 的服务端。上面配置的 rust_analyzer 就是 rust 语言的服务端,就需要我们进行服务端的安装。rust_analyzer 的服务端地址是 rust-lang/rust-analyzer,需要将服务端下载并且安装好,这样每次编写rust的时候就会享受 lsp 的服务加成了。

但是这样做有几个问题,当然也不能算问题,只是不太方便。

  1. 对于多语言使用者来说,需要手动安装多个 lsp 服务端,目前 lsp 的服务端应该是没有统一的下载安装地址,需要手动寻找;
  2. 每次服务端进行更新,都需要重新下载安装;
  3. 新换设备之后,无法开箱即用,需要重复上述的方式重新开始一次。

面对上面的不方便,你可能已经想到很多解决方法,例如写个脚本进行一键安装和更新常用的 lsp 服务端。这样基本解决了上面说的所有问题。正如你想的那样,今天的第二位主角 williamboman/mason.nvim

🗃️ mason.nvim

mason 是一个可以方便的管理 LSP 服务端,DAP 服务端,Linter 和 格式化工具的插件。安装它之后,上面所说的问题将不是问题。

为了让 mason 和 nvim-lspconfig 更好的配合,这里还需要安装另一个插件 williamboman/mason-lspconfig.nvim

同样的安装这里不多赘述,主要是进行相关的配置。这里为了区别其他的插件,我们在 lua 目录下建立新的文件夹-lsp,用来专门存放 lsp 的配置。

配置 mason

首先还是加载我们的插件。在 lsp 文件夹中新建 mason.lua 文件,在文件中新增下面的配置。配置主要是在加载插件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- mason.lua
local mason_status, mason = pcall(require, "mason")
if not mason_status then
 vim.notify("没有找到 mason")
 return
end

local nlsp_status, nvim_lsp = pcall(require, "lspconfig")
if not nlsp_status then
 vim.notify("没有找到 lspconfig")
 return
end

local mlsp_status, mason_lspconfig = pcall(require, "mason-lspconfig")
if not mlsp_status then
 vim.notify("没有找到 mason-lspconfig")
 return
end


mason.setup()
mason_lspconfig.setup({})

📤 安装 lsp 服务端

配置完成后,重新启动 nvim,此时就可以采用 mason 进行 LSP 的服务端进行管理了。只需要按下 :Mason 即可。你将会看到如下的界面。

通过界面上的帮助可以看到如何使用,通过数字可以选择不同的服务端项目,2 为 LSP , 3 为 DSP 等。今天只是使用 LSP,可以直接按 2,选择到 LSP 界面,进行 LSP 安装。仍旧是通过 jk 进行滑动。第一个安装的 lsp 服务端为 lua 语言的服务端:lua-language-server。这个是 lua 语言的语言服务,有 lsp 之后,我们之后无论是配置 nvim 还是编写 lua 都会有 lsp 服务的加持。按下 i 进行安装。

稍等片刻,安装完成。接下来就是配置,让 nvim 知道我们的 lsp 已经安装,在合适的时候进行启动。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- mason.lua
nvim_lsp.lua_ls.setup({
 on_init = function(client)
  local path = client.workspace_folders[1].name
  if not vim.loop.fs_stat(path .. "/.luarc.json") and not vim.loop.fs_stat(path .. "/.luarc.jsonc") then
   client.config.settings = vim.tbl_deep_extend("force", client.config.settings, {
    Lua = {
     runtime = {
      version = "LuaJIT",
     },
     workspace = {
      checkThirdParty = false,
      library = {
       vim.env.VIMRUNTIME,
      },
     },
    },
   })

   client.notify("workspace/didChangeConfiguration", { settings = client.config.settings })
  end
  return true
 end,
})

这样 lua 的 lsp 就配置成功了,当我们编写 lua 脚本的时候,如果发生错误就会有相关提醒。当然这只是 lsp 最基础的功能,例如代码跳转,代码补全等需要我们进行配置。

基本所有的 lsp 的配置都可以在 server_configurations.md 中找到,当然 lua_ls 也不例外,上面的配置就是直接从文档中复制的 😄。

当前的 LSP 配置已经支持代码跳转,code action 等功能。例如查看当前变量或者函数的文档,可以使用这个命令 :lua vim.lsp.buf.hover()

相关的命令还有其他

功能 命令
文档显示 :lua vim.lsp.buf.hover()
查看定义 :lua vim.lsp.buf.definition()
重命名 :lua vim.lsp.buf.rename()
查询实现 :lua vim.lsp.buf.implementation()
查询引用 :lua vim.lsp.buf.refreences()
查询声明 :lua vim.lsp.buf.declaration()
格式化 :lua vim.lsp.buf.format()
Code action :lua vim.lsp.buf.code_action()

对于这些基础功能来说,每次需要的时候都在命令模式下敲一堆,速度的确是很慢的。所以,可以将上述的命令定义为快捷键,这样每次只需要进行快捷键进行完成上述功能。

⌨️ 快捷键绑定

打开我们之前设置快捷键的配置文件 keybinding.lua,新增上述功能的配置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
-- keybinding.lua
-- lsp 快捷键设置
pluginKeys.lspKeybinding = function(mapbuf)
 -- rename
 mapbuf("n", "<leader>r", ":lua vim.lsp.buf.rename<CR>", opt)
 -- code action
 mapbuf("n", "<leader>ca", ":lua vim.lsp.buf.code_action()<CR>", opt)
 -- go to definition
 mapbuf("n", "gd", ":lua vim.lsp.buf.definition()<CR>", opt)
 -- show hover
 mapbuf("n", "gh", ":lua vim.lsp.buf.hover()<CR>", opt)
 -- format
 mapbuf("n", "<leader>=", ":lua vim.lsp.buf.format { async = true }<CR>", opt)
end

完成快捷键的配置,那么就可以将快捷键绑定到刚刚配置的 lsp 服务端了。

1
2
3
4
5
6
7
8
-- mason.lua
function LspKeybind(client, bufnr)
 local function buf_set_keymap(...)
  vim.api.nvim_buf_set_keymap(bufnr, ...)
 end
 -- 绑定快捷键
 require("keybinding").lspKeybinding(buf_set_keymap)
end

接下来可以完成快捷键的绑定。

1
2
3
4
5
6
7
-- mason.lua
nvim_lsp.lua_ls.setup({
 on_attach = LspKeybind,
 on_init = function(client)
    -- 省略其他配置
 end,
})

这样就完成了 lua 的 lsp 的配置,在编写 lua 的时候就可以使用文档查看,code Action 等功能。

目前这些 lsp 的服务都要手动下载,对于一些日常使用的服务,我们可以通过配置,在第一次加载配置的时候,当机器上没有相关的服务的时候,自动下载,这样来说,基本实现了我们上述提出的问题。

1
2
3
4
5
-- mason.lua
mason_lspconfig.setup({
 automatic_installation = true,
 ensure_installed = { "lua_ls", "rust_analyzer" },
})

这样配置,如果我们本地没有安装 lua 和 rust 的 lsp,会自动进行下载安装。

自动补全

直到目前,在 lsp 的加持下,其实编辑体验已经变得非常棒了,而且开发速率也会大幅提升,虽然 lsp 是支持自动补全功能的,但是上面其实一直没有提及。主要是单单靠 neovim 的功能还不够强大,需要插件的配置。

hrsh7th/nvim-cmp 是一个采用 lua 编写的补全引擎,通过 cmp 及 cmp 的相关插件,会将 neovim 的自动补全达到一个新的高度。

这里除了 nvim-cmp,再推荐几个 cmp 的相关插件。更多的相关插件可以在 wiki 中找到

配置补全

同样不赘述安装。在 lsp 文件夹中新建 cmp.lua 文件夹。

1
2
3
4
5
6
-- cmp.lua
local status, cmp = pcall(require, "cmp")
if not status then
    vim.notify("找不到 cmp")
    return
end

剩下了就可以将之前的补全源进行配置。

1
2
3
4
5
6
7
cmp.setup({
 sources = cmp.config.sources({
  { name = "nvim_lsp" },
 }, {
  { name = "path" },
 }),
})

此时当我们进行输入的时候就可以看到自动补全的提示了。对于自动补全的提示,上下选择并且上屏,我们可以设置快捷键,来满足我们的使用习惯。

和之前设置快捷键一样,在 keybinding.lua 中添加相关配置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
-- keybinding.lua
pluginKeys.cmp = function(cmp)
 return {
  -- 出现补全
  ["<A-.>"] = cmp.mapping(cmp.mapping.complete(), { "i", "c" }),
  -- 取消
  ["<A-,>"] = cmp.mapping({
   i = cmp.mapping.abort(),
   c = cmp.mapping.close(),
  }),
  -- 上一个
  ["<C-k>"] = cmp.mapping.select_prev_item(),
  -- 下一个
  ["<C-j>"] = cmp.mapping.select_next_item(),
  -- 确认
  ["<CR>"] = cmp.mapping.confirm({
   select = true,
   behavior = cmp.ConfirmBehavior.Replace,
  }),
 }
end

最后在 cmp.lua 中使用这些快捷键即可。

1
2
3
4
cmp.setup({
    -- 省略其他配置
    mapping = require("keybinding").cmp(cmp),
})

这样便可以完成自动补全的配置了。

小结

目前为止,已经完成 nvim 的 lsp 的相关配置,并且添加了自动补全。篇幅限制,剩下如何美化 lsp 提示,美化自动补全等我们下篇再说。

❌
❌