NVIM 配置打造

NVIM 配置

NVIM 安装#

Copy
# 安装 NVIM brew install nvim brew install ranger # 需要有 python3 支持 brew install python3 brew install node # 对 nvim 添加 python 支持 pip3 install neovim pynvim # 输入 nvim 检测 :checkhealth

image-20220106235325902

NVIM 配置#

init.vim#

Copy
mkdir -p ~/.config/nvim nvim ~/.config/nvim/init.vim " 配置文件导入 "{{{ " --------------------------------------------------------------------- " 基础使用习惯配置 runtime ./vimrc.vim " 插件管理 "./lua/plugins.lua lua require('plugins') " 按键映射 runtime ./maps.vim

vimrc.vim#

Copy
nvim ~/.config/nvim/vimrc.vim "----vim 个人使用习惯配置start------ set encoding=UTF-8 " leader设置成空格 let mapleader=" " " 使用鼠标 set mouse=c " 显示行号 set nu " 相对行号 set relativenumber " tab=4个空格 set tabstop=4 set shiftwidth=4 " 改变 vim中光标的形状 let g:db_ui_use_nerd_fonts=1 let &t_SI.="\e[5 q" "SI = INSERT mode let &t_SR.="\e[4 q" "SR = REPLACE mode let &t_EI.="\e[1 q" "EI = NORMAL mode (ELSE) " 高度光标所在行 "set cursorline " 设置不换行 "set nowrap set wrap " 显示按下的按键 set showcmd " 按tab显示菜单 set wildmenu " 不需要备份文件 set nobackup "----vim 个人使用习惯配置end------ "ssh 远程粘贴板 if executable('clipboard-provider') let g:clipboard = { \ 'name': 'myClipboard', \ 'copy': { \ '+': 'clipboard-provider copy', \ '*': 'env COPY_PROVIDERS=tmux clipboard-provider copy', \ }, \ 'paste': { \ '+': 'clipboard-provider paste', \ '*': 'env COPY_PROVIDERS=tmux clipboard-provider paste', \ }, \ } endif "随机选一个颜色风格 function RandomColorScheme() let mycolors = split(globpath(&rtp,"**/colors/*.vim"),"\n") exe 'so ' . mycolors[localtime() % len(mycolors)] unlet mycolors endfunction "call RandomColorScheme() :command NewColor call RandomColorScheme() " 文件类型设置 "{{{ " --------------------------------------------------------------------- " JSX 语法高亮 " set filetypes as typescriptreact autocmd BufNewFile,BufRead *.tsx,*.jsx,*.js set filetype=typescriptreact " 添加头部文件 function HeaderPython() call setline(1, "#!/usr/bin/env python") call append(1, "# -*- coding: utf-8 -*-") call append(2, "# SR @ " . strftime('%Y-%m-%d %T', localtime())) normal G normal o normal o endf autocmd bufnewfile *.py call HeaderPython()

maps.vim#

Copy
nvim ~/.config/nvim/maps.vim "------------------------------------------------------------------------------- " window "------------------------------------------------------------------------------- " Split window nmap ss :split<Return><C-w>w nmap sv :vsplit<Return><C-w>w " Move window "nmap <Space> <C-w>w "map s<left> <C-w>h "map s<up> <C-w>k "map s<down> <C-w>j "map s<right> <C-w>l map sh <C-w>h map sk <C-w>k map sj <C-w>j map sl <C-w>l " Resize window " 在mac/linux中使用Alt键,在webssh " 中alt没用,就使用Ctrl,WEBSSH主要的WINDOWS中使用 nmap <M-left> <C-w>< nmap <C-left> <C-w>< nmap s<left> <C-w>< nmap <M-right> <C-w>> nmap <C-right> <C-w>> nmap s<right> <C-w>> nmap <M-up> <C-w>+ nmap <C-up> <C-w>+ nmap s<up> <C-w>+ nmap <M-down> <C-w>- nmap <C-down> <C-w>- nmap s<down> <C-w>- " 插入模式移动光标 inoremap <C-h> <Left> inoremap <C-j> <Down> inoremap <C-k> <Up> inoremap <C-l> <Right> inoremap <C-d> <Delete> " hh在我用的单词里出现的频率极低,其实感觉home用的没有end多,统一风格都用大写的吧 "inoremap HH <Home> " 单词中包含ll的太多了,所以用大写LL "inoremap LL <End> " jk <Esc> inoremap jk <Esc> " 插入模式鼠标滚轮抵消,不然会出现滚动鼠标录入了一堆5j5k inoremap 5k <Esc> inoremap 5j <Esc> inoremap 9<CR> <Esc>a " 快速跳转行首与行尾 nnoremap L $ nnoremap H ^ " 向下5行 noremap <C-j> 5j " 向上5行 noremap <C-k> 5k " 保 存 noremap <C-s> :w<CR> noremap s :w<CR> " Coc智能处理,使用IDEA Alt+Enter 同样按键 "noremap <M-Enter> :CocAction<CR> inoremap <C-s> <ESC> :w<CR> " 代码格式化 noremap <leader>f :Format<CR> noremap <leader>r :luafile ~/.wp/lua/run.lua<CR> " 强制退出 map Q :q<CR> "map qq :q<CR> " 重新加载设置 map R :source $MYVIMRC<CR> "自动关闭标签 inoremap <buffer> <C-v> <esc>yiwi<lt><esc>ea></><esc>hpF>i set iskeyword+=<,> iab <h1> <lt>h1> <lt>/h1><esc>5ha " 全选 nmap <C-a> gg<S-v>G " 加/减数字1 nnoremap + <C-a> nnoremap - <C-x> "------------------------------------------------------------------------------- " Buffers "------------------------------------------------------------------------------- " Open current directory " 插入模式移动光标 inoremap <C-h> <Left> inoremap <C-j> <Down> inoremap <C-k> <Up> inoremap <C-l> <Right> inoremap <C-d> <Delete> " hh在我用的单词里出现的频率极低,其实感觉home用的没有end多,统一风格都用大写的吧 "inoremap HH <Home> " 单词中包含ll的太多了,所以用大写LL "inoremap LL <End> " jk <Esc> inoremap jk <Esc> " 插入模式鼠标滚轮抵消,不然会出现滚动鼠标录入了一堆5j5k inoremap 5k <Esc> inoremap 5j <Esc> inoremap 9<CR> <Esc>a " 快速跳转行首与行尾 nnoremap L $ nnoremap H ^ " 向下5行 noremap <C-j> 5j " 向上5行 noremap <C-k> 5k " 保存 noremap <C-s> :w<CR> " Coc智能处理,使用IDEA Alt+Enter 同样按键 noremap <M-Enter> :CocAction<CR> inoremap <C-s> <ESC> :w<CR> " 代码格式化 "noremap <leader>f :Format<CR> " 强制退出 map Q :q<CR> " 重新加载设置 map R :source $MYVIMRC<CR> "自动关闭标签 inoremap <buffer> <C-v> <esc>yiwi<lt><esc>ea></><esc>hpF>i set iskeyword+=<,> iab <h1> <lt>h1> <lt>/h1><esc>5ha " 全选 nmap <C-a> gg<S-v>G " 加/减数字1 nnoremap + <C-a> nnoremap - <C-x> "------------------------------------------------------------------------------- " Buffers "------------------------------------------------------------------------------- " Open current directory "nmap te :tabedit "nmap <S-Tab> :tabprev<Return> nmap <S-Tab> :bprev<Return> "nmap <Tab> :tabnext<Return> nmap <Tab> :bnext<Return> " 窗口管理器 " invoke with '-' nmap - <Plug>(choosewin) "nmap sw <Plug>(choosewin) "nmap <leader>w <Plug>(choosewin)

PS:一些插件的快捷键在末尾 当所有插件都配置完毕的时候将快捷键加入该文件中

packer 插件管理器#

Copy
mkdir -p ~/.config/nvim/lua nvim ~/.config/nvim/plugins.lua ---@diagnostic disable: undefined-global --在没有安装packer的电脑上,自动安装packer插件 local fn = vim.fn local install_path = fn.stdpath('data')..'/site/pack/packer/start/packer.nvim' if fn.empty(fn.glob(install_path)) > 0 then --fn.system({'git', 'clone', '--depth', '1', 'https://github.com/wbthomason/packer.nvim', install_path}) --默认地址 fn.system({'git', 'clone', '--depth', '1', 'https://codechina.csdn.net/mirrors/wbthomason/packer.nvim.git', install_path}) --csdn加速镜像 vim.cmd 'packadd packer.nvim' end -- Only required if you have packer configured as `opt` vim.cmd [[packadd packer.nvim]] return require('packer').startup({ function() use 'wbthomason/packer.nvim'-- Packer can manage itself end, config = { max_jobs = 16, git = { default_url_format = "https://github.com.cnpmjs.org/%s" }, display = { open_fn = function() return require('packer.util').float({ border = 'single' }) end } } }) # 插件安装方式 PackerInstall

插件配置#

字体配置#

Copy
# 终端安装该字体并且选择该字体防止乱码 brew tap homebrew/cask-fonts brew install font-hack-nerd-font --cask

lualine标签栏美化#

安装#

Copy
nvim ~/.config/nvim/lua/plugins.lua --状态栏插件 use { "nvim-lualine/lualine.nvim", requires = {"kyazdani42/nvim-web-devicons", opt = true} }

配置#

Copy
nvim ~/.config/nvim/after/plugin/lualine.lua local status, lualine = pcall(require, "lualine") if (not status) then return end lualine.setup { options = { icons_enabled = true, theme = "auto", component_separators = {left = "", right = ""}, section_separators = {left = "", right = ""}, disabled_filetypes = {}, always_divide_middle = true }, sections = { lualine_a = {"mode"}, lualine_b = { "branch", "diff" --{"diagnostics", sources = {"nvim_lsp"}} }, lualine_c = {"filename"}, lualine_x = { {"diagnostics", sources = {"nvim_lsp"}, symbols = {error = " ", warn = " ", info = " ", hint = " "}}, "encoding", "fileformat", "filetype" }, lualine_y = {"progress"}, lualine_z = {"location"} }, inactive_sections = { lualine_a = {}, lualine_b = {}, lualine_c = {"filename"}, lualine_x = {"location"}, lualine_y = {}, lualine_z = {} }, tabline = {}, extensions = {} }

image-20220107013051893

bufferline状态栏#

安装#

Copy
-- bufferline 显示标签页,与lualine配合使用 use "akinsho/bufferline.nvim"

配置#

Copy
nvim ~/.config/nvim/after/plugin/bufferline.lua local status, bufferline = pcall(require, "bufferline") if (not status) then return end vim.opt.termguicolors = true bufferline.setup { options = { --numbers = "ordinal" | "buffer_id" | "both" | function({ ordinal, id, lower, raise }): string, --numbers = "both", --- @deprecated, please specify numbers as a function to customize the styling --number_style = "superscript" | "subscript" | "" | { "none", "subscript" }, -- buffer_id at index 1, ordinal at index 2 --number_style = "none", close_command = "bdelete! %d", -- can be a string | function, see "Mouse actions" right_mouse_command = "bdelete! %d", -- can be a string | function, see "Mouse actions" left_mouse_command = "buffer %d", -- can be a string | function, see "Mouse actions" middle_mouse_command = nil, -- can be a string | function, see "Mouse actions" -- NOTE: this plugin is designed with this icon in mind, -- and so changing this is NOT recommended, this is intended -- as an escape hatch for people who cannot bear it for whatever reason indicator_icon = "", buffer_close_icon = "", modified_icon = "●", close_icon = "", left_trunc_marker = "", right_trunc_marker = "", --- name_formatter can be used to change the buffer's label in the bufferline. --- Please note some names can/will break the --- bufferline so use this at your discretion knowing that it has --- some limitations that will *NOT* be fixed. name_formatter = function(buf) -- buf contains a "name", "path" and "bufnr" -- remove extension from markdown files for example if buf.name:match("%.md") then return vim.fn.fnamemodify(buf.name, ":t:r") end end, max_name_length = 15, max_prefix_length = 12, -- prefix used when a buffer is de-duplicated tab_size = 15, --diagnostics = false | "nvim_lsp" | "coc", diagnostics = "nvim_lsp", diagnostics_update_in_insert = false, --[[diagnostics_indicator = function(count, level, diagnostics_dict, context) return "(" .. count .. ")" end,]] -- rest of config ... --- count is an integer representing total count of errors --- level is a string "error" | "warning" --- diagnostics_dict is a dictionary from error level ("error", "warning" or "info")to number of errors for each level. --- this should return a string --- Don't get too fancy as this function will be executed a lot diagnostics_indicator = function(count, level, diagnostics_dict, context) local icon = level:match("error") and " " or " " return " " .. icon .. count end, -- NOTE: this will be called a lot so don't do any heavy processing here custom_filter = function(buf_number) --如果是defx则隐藏 local finded, _ = string.find(vim.bo[buf_number].filetype, "defx") if finded ~= nil then return false end return true end, --offsets = {{filetype = "NvimTree", text = "File Explorer" | function , text_align = "left" | "center" | "right"}}, --show_buffer_icons = true | false, -- disable filetype icons for buffers show_buffer_icons = true, -- disable filetype icons for buffers --show_buffer_close_icons = true | false, show_buffer_close_icons = false, --show_close_icon = true | false, show_close_icon = false, --show_tab_indicators = true | false, show_tab_indicators = true, persist_buffer_sort = true, -- whether or not custom sorted buffers should persist -- can also be a table containing 2 custom separators -- [focused and unfocused]. eg: { '|', '|' } --separator_style = "slant" | "thick" | "thin" | {"any", "any"}, separator_style = "thin", --enforce_regular_tabs = false | true, enforce_regular_tabs = false, --always_show_bufferline = true | false, always_show_bufferline = true --[[sort_by = "id" | "extension" | "relative_directory" | "directory" | "tabs" | function(buffer_a, buffer_b) -- add custom logic return buffer_a.modified > buffer_b.modified end]] } } --按键映射 --nnoremap <silent> gb :BufferLinePick<CR> vim.api.nvim_set_keymap("n", "gb", "<Cmd>BufferLinePick<CR>", {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "<leader>1", "<Cmd>BufferLineGoToBuffer 1<CR>", {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "<leader>2", "<Cmd>BufferLineGoToBuffer 2<CR>", {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "<leader>3", "<Cmd>BufferLineGoToBuffer 3<CR>", {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "<leader>4", "<Cmd>BufferLineGoToBuffer 4<CR>", {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "<leader>5", "<Cmd>BufferLineGoToBuffer 5<CR>", {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "<leader>6", "<Cmd>BufferLineGoToBuffer 6<CR>", {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "<leader>7", "<Cmd>BufferLineGoToBuffer 7<CR>", {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "<leader>8", "<Cmd>BufferLineGoToBuffer 8<CR>", {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "<leader>9", "<Cmd>BufferLineGoToBuffer 9<CR>", {noremap = true, silent = true})

image-20220107013221086

dashboard开屏插件#

安装#

Copy
use {"glepnir/dashboard-nvim"}

配置#

Copy
nvim ~/.config/nvim/after/plugin/dashboard.lua local vim = vim vim.g.dashboard_custom_header = { "", " ⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⠞⠉⢉⣭⣿⣿⠿⣳⣤⠴⠖⠛⣛⣿⣿⡷⠖⣶⣤⡀⠀⠀⠀ ", " ⠀⠀⠀⠀⠀⠀⠀⣼⠁⢀⣶⢻⡟⠿⠋⣴⠿⢻⣧⡴⠟⠋⠿⠛⠠⠾⢛⣵⣿⠀⠀⠀⠀ ", " ⣼⣿⡿⢶⣄⠀⢀⡇⢀⡿⠁⠈⠀⠀⣀⣉⣀⠘⣿⠀⠀⣀⣀⠀⠀⠀⠛⡹⠋⠀⠀⠀⠀ ", " ⣭⣤⡈⢑⣼⣻⣿⣧⡌⠁⠀⢀⣴⠟⠋⠉⠉⠛⣿⣴⠟⠋⠙⠻⣦⡰⣞⠁⢀⣤⣦⣤⠀ ", " ⠀⠀⣰⢫⣾⠋⣽⠟⠑⠛⢠⡟⠁⠀⠀⠀⠀⠀⠈⢻⡄⠀⠀⠀⠘⣷⡈⠻⣍⠤⢤⣌⣀ ", " ⢀⡞⣡⡌⠁⠀⠀⠀⠀⢀⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⢿⡀⠀⠀⠀⠸⣇⠀⢾⣷⢤⣬⣉ ", " ⡞⣼⣿⣤⣄⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⣿⠀⠸⣿⣇⠈⠻ ", " ⢰⣿⡿⢹⠃⠀⣠⠤⠶⣼⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⣿⠀⠀⣿⠛⡄⠀ ", " ⠈⠉⠁⠀⠀⠀⡟⡀⠀⠈⡗⠲⠶⠦⢤⣤⣤⣄⣀⣀⣸⣧⣤⣤⠤⠤⣿⣀⡀⠉⣼⡇⠀ ", " ⣿⣴⣴⡆⠀⠀⠻⣄⠀⠀⠡⠀⠀⠀⠈⠛⠋⠀⠀⠀⡈⠀⠻⠟⠀⢀⠋⠉⠙⢷⡿⡇⠀ ", " ⣻⡿⠏⠁⠀⠀⢠⡟⠀⠀⠀⠣⡀⠀⠀⠀⠀⠀⢀⣄⠀⠀⠀⠀⢀⠈⠀⢀⣀⡾⣴⠃⠀ ", " ⢿⠛⠀⠀⠀⠀⢸⠁⠀⠀⠀⠀⠈⠢⠄⣀⠠⠼⣁⠀⡱⠤⠤⠐⠁⠀⠀⣸⠋⢻⡟⠀⠀ ", " ⠈⢧⣀⣤⣶⡄⠘⣆⠀⠀⠀⠀⠀⠀⠀⢀⣤⠖⠛⠻⣄⠀⠀⠀⢀⣠⡾⠋⢀⡞⠀⠀⠀ ", " ⠀⠀⠻⣿⣿⡇⠀⠈⠓⢦⣤⣤⣤⡤⠞⠉⠀⠀⠀⠀⠈⠛⠒⠚⢩⡅⣠⡴⠋⠀⠀⠀⠀ ", " ⠀⠀⠀⠈⠻⢧⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⣻⠿⠋⠀⠀⠀⠀⠀⠀ ", " ⠀⠀⠀⠀⠀⠀⠉⠓⠶⣤⣄⣀⡀⠀⠀⠀⠀⠀⢀⣀⣠⡴⠖⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀ ", " ", "", } vim.g.dashboard_custom_section = { a = { description = { "  新 文 件 SPC fo" }, command = "DashboardNewFile", }, b = { description = { "  查找文件 SPC ff" }, -- command = "Telescope fd", command = "Telescope fd find_command=fd,--hidden", }, c = { description = { "  已用文件 SPC fh" }, command = "Telescope oldfiles", }, d = { description = { "  跳转标记 SPC fm" }, command = "Telescope marks", }, e = { description = { "  查看内容 SPC fw" }, command = "Telescope live_grep", }, f = { description = { "  查看主题 SPC tc" }, command = "Telescope colorscheme", }, g = { description = { " ∂ 查看命令 SPC fc" }, command = "Telescope commands", }, h = { description = { "  查看帮助 SPC fa" }, command = "Telescope man_pages", }, } vim.g.dashboard_disable_at_vimenter = 0 vim.g.dashboard_footer_icon = " 🐬 " vim.g.dashboard_preview_command = "bat" -- vim.g.dashboard_preview_pipeline = "lolcat -F 0.3" vim.g.dashboard_session_directory = "~/.config/nvim/.sessions" vim.g.dashboard_preview_file_height = 12 vim.g.dashboard_preview_file_width = 80 vim.g.dashboard_default_executive = "Telescope"

image-20220107014239249

defx文件插件#

安装#

Copy
# 安装 -- 文件管理插件,类似与ranger。 use { "Shougo/defx.nvim", requires = { "kristijanhusak/defx-icons", -- dexf文件管理图标支持 "t9md/vim-choosewin" -- 窗口选择器,可以让defx使用i打开文件 } }

配置#

Copy
mkdir -p ~/.config/nvim/after/plugin nvim ~/.config/nvim/after/plugin/defx.rc.vim # 配置如下 if !exists('g:loaded_defx') | finish | endif " Define mappings "cnoreabbrev sf Defx -listed -new " \ -columns=indent:mark:icon:icons:filename:git:size " \ -buffer-name=tab`tabpagenr()`<CR> nnoremap <silent>sf :<C-u>Defx -listed -resume \ -columns=indent:mark:icon:icons:filename:git:size \ -buffer-name=tab`tabpagenr()` \ `expand('%:p:h')` -search=`expand('%:p')`<CR> "nnoremap <silent>fi :<C-u>Defx -new `expand('%:p:h')` -search=`expand('%:p')`<CR> autocmd FileType defx call s:defx_my_settings() function! s:defx_my_settings() abort " Define mappings nnoremap <silent><buffer><expr> <CR> \ defx#do_action('open') nnoremap <silent><buffer><expr> yy \ defx#do_action('copy') nnoremap <silent><buffer><expr> dd \ defx#do_action('move') nnoremap <silent><buffer><expr> pp \ defx#do_action('paste') nnoremap <silent><buffer><expr> l \ defx#do_action('open') nnoremap <silent><buffer><expr> <Right> \ defx#do_action('open') nnoremap <silent><buffer><expr> E \ defx#do_action('open', 'vsplit') nnoremap <silent><buffer><expr> n \ defx#do_action('open', 'pedit') nnoremap <silent><buffer><expr> i \ defx#do_action('open', 'choose') nnoremap <silent><buffer><expr> o \ defx#do_action('open_or_close_tree') nnoremap <silent><buffer><expr> K \ defx#do_action('new_directory') nnoremap <silent><buffer><expr> N \ defx#do_action('new_file') nnoremap <silent><buffer><expr> M \ defx#do_action('new_multiple_files') nnoremap <silent><buffer><expr> C \ defx#do_action('toggle_columns', \ 'mark:indent:icon:filename:type:size:time') nnoremap <silent><buffer><expr> S \ defx#do_action('toggle_sort', 'time') nnoremap <silent><buffer><expr> dD \ defx#do_action('remove') nnoremap <silent><buffer><expr> a \ defx#do_action('rename') nnoremap <silent><buffer><expr> ! \ defx#do_action('execute_command') nnoremap <silent><buffer><expr> x \ defx#do_action('execute_system') nnoremap <silent><buffer><expr> YY \ defx#do_action('yank_path') nnoremap <silent><buffer><expr> . \ defx#do_action('toggle_ignored_files') nnoremap <silent><buffer><expr> <BS> \ defx#do_action('toggle_ignored_files') nnoremap <silent><buffer><expr> ; \ defx#do_action('repeat') nnoremap <silent><buffer><expr> h \ defx#do_action('cd', ['..']) nnoremap <silent><buffer><expr> <Left> \ defx#do_action('cd', ['..']) nnoremap <silent><buffer><expr> ~ \ defx#do_action('cd') nnoremap <silent><buffer><expr> gh \ defx#do_action('cd') nnoremap <silent><buffer><expr> gn \ defx#do_action('cd',['~/.config/nvim']) nnoremap <silent><buffer><expr> q \ defx#do_action('quit') nnoremap <silent><buffer><expr> <Space> \ defx#do_action('toggle_select') . 'j' nnoremap <silent><buffer><expr> m \ defx#do_action('toggle_select') . 'j' nnoremap <silent><buffer><expr> vv \ defx#do_action('toggle_select_all') nnoremap <silent><buffer><expr> * \ defx#do_action('toggle_select_all') nnoremap <silent><buffer><expr> j \ line('.') == line('$') ? 'gg' : 'j' nnoremap <silent><buffer><expr> k \ line('.') == 1 ? 'G' : 'k' nnoremap <silent><buffer><expr> <C-l> \ defx#do_action('redraw') nnoremap <silent><buffer><expr> <C-g> \ defx#do_action('print') nnoremap <silent><buffer><expr> cd \ defx#do_action('change_vim_cwd') endfunction call defx#custom#column('icon', { \ 'directory_icon': '▸', \ 'opened_icon': '▾', \ 'root_icon': ' ', \ }) call defx#custom#column('git', 'indicators', { \ 'Modified' : 'M', \ 'Staged' : '✚', \ 'Untracked' : '✭', \ 'Renamed' : '➜', \ 'Unmerged' : '═', \ 'Ignored' : '☒', \ 'Deleted' : '✖', \ 'Unknown' : '?' \ })

defx-icons配置#

Copy
nvim ~/.config/nvim/after/plugin/defx-icons.rc.vim " 增加图标的宽度,来解决图标 与文件名重叠的问题 let g:defx_icons_column_length = 2 " 因为图标配置需要优化级比较高,所以要在init.vim中增加如下一行: nvim ~/.config/nvim/init.vim source ~/.config/nvim/after/plugin/defx-icons.rc.vim

vim-choosewin配置#

Copy
" 这是一个窗口选择插件. nvim ~/.config/nvim/after/plugin/vim-choosewin.rc.vim "window选择器 " if you want to use overlay feature let g:choosewin_overlay_enable = 1

快捷键#

sf 启动文件管理器

image-20220107002128223

l进入下一层目录

image-20220107002301742

h 返回上一层目录

image-20220107002344062

K(大写)创建目录

image-20220107002503152

N 创建新的文件

image-20220107002641179

空格/m 文件选择

image-20220107003047227

快捷键 操作
j 向下一行
k 向上一行
yy 复制
pp 粘贴
dd 剪切
dD 删除
vv 文件全选

LSP 代码补全插件#

安装#

Copy
--Nvim LSP 客户端的快速入门配置 use "neovim/nvim-lspconfig" use { "hrsh7th/nvim-cmp", requires = { "hrsh7th/cmp-nvim-lsp", --neovim 内置 LSP 客户端的 nvim-cmp 源 --以下插件可选,可以根据个人喜好删减 "onsails/lspkind-nvim", --美化自动完成提示信息 "hrsh7th/cmp-buffer", --从buffer中智能提示 "hrsh7th/cmp-nvim-lua", --nvim-cmp source for neovim Lua API. "octaltree/cmp-look", --用于完成英语单词 "hrsh7th/cmp-path", --自动提示硬盘上的文件 "hrsh7th/cmp-calc", --输入数学算式(如1+1=)自动计算 "f3fora/cmp-spell", --nvim-cmp 的拼写源基于 vim 的拼写建议 "hrsh7th/cmp-emoji", --输入: 可以显示表情 } } -- 代码段提示 use { "L3MON4D3/LuaSnip", requires = { "saadparwaiz1/cmp_luasnip", -- Snippets source for nvim-cmp "rafamadriz/friendly-snippets" --代码段合集 } }

配置#

Copy
nvim ~/.config/nvim/after/plugin/lspconfig.rc.vim if !exists('g:lspconfig') finish endif lua << EOF --提示信息自定义图标 -- icon vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with( vim.lsp.diagnostic.on_publish_diagnostics, { underline = true, -- This sets the spacing and the prefix, obviously. virtual_text = { spacing = 4, prefix = '' } } ) EOF

nvim-cmp配置#

Copy
nvim ~/.config/nvim/after/plugin/nvim-cmp.lua local status, nvim_lsp = pcall(require, "lspconfig") if (not status) then return end --local nvim_lsp = require('lspconfig') --typescript支持 require("lspconf.typescript") --json支持 --require("lspconf.json") --lua --require("lspconf.lua") --普通的语言支持 --require("lspconf.common") -- Set completeopt to have a better completion experience vim.o.completeopt = "menuone,noselect" -- luasnip setup local luasnip = require "luasnip" local lspkind = require("lspkind") -- nvim-cmp setup local cmp = require "cmp" -- 自动提示1 详情信息 local cmpFormat1 = function(entry, vim_item) -- fancy icons and a name of kind vim_item.kind = require("lspkind").presets.default[vim_item.kind] .. " " .. vim_item.kind -- set a name for each source vim_item.menu = ({ buffer = "[Buffer]", nvim_lsp = "[LSP]", ultisnips = "[UltiSnips]", nvim_lua = "[Lua]", cmp_tabnine = "[TabNine]", look = "[Look]", path = "[Path]", spell = "[Spell]", calc = "[Calc]", emoji = "[Emoji]" })[entry.source.name] return vim_item end -- 自动提示2 简洁信息 local cmpFormat2 = function(entry, vim_item) vim_item.kind = lspkind.presets.default[vim_item.kind] return vim_item end -- 自动提示3 详情信息 local cmpFormat3 = function(entry, vim_item) -- fancy icons and a name of kind vim_item.kind = require("lspkind").presets.default[vim_item.kind] .. "" -- set a name for each source vim_item.menu = ({ buffer = "[Buffer]", nvim_lsp = "", ultisnips = "[UltiSnips]", nvim_lua = "[Lua]", cmp_tabnine = "[TabNine]", look = "[Look]", path = "[Path]", spell = "[Spell]", calc = "[Calc]", emoji = "[Emoji]" })[entry.source.name] return vim_item end ------修复2021年10月12日 nvim-cmp.luaattempt to index field 'menu' (a nil value)--------- --重写插件方法,为了实现function 后,自动追加() local keymap = require("cmp.utils.keymap") cmp.confirm = function(option) option = option or {} local e = cmp.core.view:get_selected_entry() or (option.select and cmp.core.view:get_first_entry() or nil) if e then cmp.core:confirm( e, { behavior = option.behavior }, function() local myContext = cmp.core:get_context({reason = cmp.ContextReason.TriggerOnly}) cmp.core:complete(myContext) --function() 自动增加() if e and e.resolved_completion_item and (e.resolved_completion_item.kind == 3 or e.resolved_completion_item.kind == 2) then vim.api.nvim_feedkeys(keymap.t("()<Left>"), "n", true) end end ) return true else if vim.fn.complete_info({"selected"}).selected ~= -1 then keymap.feedkeys(keymap.t("<C-y>"), "n") return true end return false end end --------------- cmp.setup { formatting = { format = cmpFormat1 }, snippet = { expand = function(args) require("luasnip").lsp_expand(args.body) end }, mapping = { ["<C-p>"] = cmp.mapping.select_prev_item(), ["<C-n>"] = cmp.mapping.select_next_item(), ["<C-d>"] = cmp.mapping.scroll_docs(-4), ["<C-f>"] = cmp.mapping.scroll_docs(4), ["<C-Space>"] = cmp.mapping.complete(), ["<C-e>"] = cmp.mapping.close(), ["<CR>"] = cmp.mapping.confirm { behavior = cmp.ConfirmBehavior.Replace, select = false }, ['<Tab>'] = function(fallback) if cmp.visible() then cmp.select_next_item() elseif luasnip.expand_or_jumpable() then vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<Plug>luasnip-expand-or-jump', true, true, true), '') else fallback() end end, ['<S-Tab>'] = function(fallback) if cmp.visible() then cmp.select_prev_item() elseif luasnip.jumpable(-1) then vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<Plug>luasnip-jump-prev', true, true, true), '') else fallback() end end }, sources = { {name = "nvim_lsp"}, {name = "luasnip"}, --{name = "nvim_lua"}, { name = "buffer", options = { get_bufnrs = function() return vim.api.nvim_list_bufs() end } }, --{name = "look"}, {name = "path"} --{name = "cmp_tabnine"}, --{name = "calc"}, --{name = "spell"}, --{name = "emoji"} } }

LuaSnip配置#

Copy
nvim ~/.config/nvim/after/plugin/snippets.lua local ls = require("luasnip") -- some shorthands... local s = ls.snippet local sn = ls.snippet_node local t = ls.text_node local i = ls.insert_node local f = ls.function_node local c = ls.choice_node local d = ls.dynamic_node local l = require("luasnip.extras").lambda local r = require("luasnip.extras").rep local p = require("luasnip.extras").partial local m = require("luasnip.extras").match local n = require("luasnip.extras").nonempty local dl = require("luasnip.extras").dynamic_lambda local fmt = require("luasnip.extras.fmt").fmt local fmta = require("luasnip.extras.fmt").fmta local types = require("luasnip.util.types") local conds = require("luasnip.extras.expand_conditions") -- Every unspecified option will be set to the default. ls.config.set_config({ history = true, -- Update more often, :h events for more info. updateevents = "TextChanged,TextChangedI", ext_opts = { [types.choiceNode] = { active = { virt_text = { { "choiceNode", "Comment" } }, }, }, }, -- treesitter-hl has 100, use something higher (default is 200). ext_base_prio = 300, -- minimal increase in priority. ext_prio_increase = 1, enable_autosnippets = true, }) -- args is a table, where 1 is the text in Placeholder 1, 2 the text in -- placeholder 2,... local function copy(args) return args[1] end -- 'recursive' dynamic snippet. Expands to some text followed by itself. local rec_ls rec_ls = function() return sn( nil, c(1, { -- Order is important, sn(...) first would cause infinite loop of expansion. t(""), sn(nil, { t({ "", "\t\\item " }), i(1), d(2, rec_ls, {}) }), }) ) end -- complicated function for dynamicNode. local function jdocsnip(args, _, old_state) local nodes = { t({ "/**", " * " }), i(1, "A short Description"), t({ "", "" }), } -- These will be merged with the snippet; that way, should the snippet be updated, -- some user input eg. text can be referred to in the new snippet. local param_nodes = {} if old_state then nodes[2] = i(1, old_state.descr:get_text()) end param_nodes.descr = nodes[2] -- At least one param. if string.find(args[2][1], ", ") then vim.list_extend(nodes, { t({ " * ", "" }) }) end local insert = 2 for indx, arg in ipairs(vim.split(args[2][1], ", ", true)) do -- Get actual name parameter. arg = vim.split(arg, " ", true)[2] if arg then local inode -- if there was some text in this parameter, use it as static_text for this new snippet. if old_state and old_state[arg] then inode = i(insert, old_state["arg" .. arg]:get_text()) else inode = i(insert) end vim.list_extend( nodes, { t({ " * @param " .. arg .. " " }), inode, t({ "", "" }) } ) param_nodes["arg" .. arg] = inode insert = insert + 1 end end if args[1][1] ~= "void" then local inode if old_state and old_state.ret then inode = i(insert, old_state.ret:get_text()) else inode = i(insert) end vim.list_extend( nodes, { t({ " * ", " * @return " }), inode, t({ "", "" }) } ) param_nodes.ret = inode insert = insert + 1 end if vim.tbl_count(args[3]) ~= 1 then local exc = string.gsub(args[3][2], " throws ", "") local ins if old_state and old_state.ex then ins = i(insert, old_state.ex:get_text()) else ins = i(insert) end vim.list_extend( nodes, { t({ " * ", " * @throws " .. exc .. " " }), ins, t({ "", "" }) } ) param_nodes.ex = ins insert = insert + 1 end vim.list_extend(nodes, { t({ " */" }) }) local snip = sn(nil, nodes) -- Error on attempting overwrite. snip.old_state = param_nodes return snip end -- Make sure to not pass an invalid command, as io.popen() may write over nvim-text. local function bash(_, _, command) local file = io.popen(command, "r") local res = {} for line in file:lines() do table.insert(res, line) end return res end -- Returns a snippet_node wrapped around an insert_node whose initial -- text value is set to the current date in the desired format. local date_input = function(args, state, fmt) local fmt = fmt or "%Y-%m-%d" return sn(nil, i(1, os.date(fmt))) end ls.snippets = { -- When trying to expand a snippet, luasnip first searches the tables for -- each filetype specified in 'filetype' followed by 'all'. -- If ie. the filetype is 'lua.c' -- - luasnip.lua -- - luasnip.c -- - luasnip.all -- are searched in that order. all = { -- trigger is fn. s("fn", { -- Simple static text. t("//Parameters: "), -- function, first parameter is the function, second the Placeholders -- whose text it gets as input. f(copy, 2), t({ "", "function " }), -- Placeholder/Insert. i(1), t("("), -- Placeholder with initial text. i(2, "int foo"), -- Linebreak t({ ") {", "\t" }), -- Last Placeholder, exit Point of the snippet. EVERY 'outer' SNIPPET NEEDS Placeholder 0. i(0), t({ "", "}" }), }), s("class", { -- Choice: Switch between two different Nodes, first parameter is its position, second a list of nodes. c(1, { t("public "), t("private "), }), t("class "), i(2), t(" "), c(3, { t("{"), -- sn: Nested Snippet. Instead of a trigger, it has a position, just like insert-nodes. !!! These don't expect a 0-node!!!! -- Inside Choices, Nodes don't need a position as the choice node is the one being jumped to. sn(nil, { t("extends "), i(1), t(" {"), }), sn(nil, { t("implements "), i(1), t(" {"), }), }), t({ "", "\t" }), i(0), t({ "", "}" }), }), -- Use a dynamic_node to interpolate the output of a -- function (see date_input above) into the initial -- value of an insert_node. s("novel", { t("It was a dark and stormy night on "), d(1, date_input, {}, "%A, %B %d of %Y"), t(" and the clocks were striking thirteen."), }), -- Parsing snippets: First parameter: Snippet-Trigger, Second: Snippet body. -- Placeholders are parsed into choices with 1. the placeholder text(as a snippet) and 2. an empty string. -- This means they are not SELECTed like in other editors/Snippet engines. ls.parser.parse_snippet( "lspsyn", "Wow! This ${1:Stuff} really ${2:works. ${3:Well, a bit.}}" ), -- When wordTrig is set to false, snippets may also expand inside other words. ls.parser.parse_snippet( { trig = "te", wordTrig = false }, "${1:cond} ? ${2:true} : ${3:false}" ), -- When regTrig is set, trig is treated like a pattern, this snippet will expand after any number. ls.parser.parse_snippet({ trig = "%d", regTrig = true }, "A Number!!"), -- Using the condition, it's possible to allow expansion only in specific cases. s("cond", { t("will only expand in c-style comments"), }, { condition = function(line_to_cursor, matched_trigger, captures) -- optional whitespace followed by // return line_to_cursor:match("%s*//") end, }), -- there's some built-in conditions in "luasnip.extras.expand_conditions". s("cond2", { t("will only expand at the beginning of the line"), }, { condition = conds.line_begin, }), -- The last entry of args passed to the user-function is the surrounding snippet. s( { trig = "a%d", regTrig = true }, f(function(_, snip) return "Triggered with " .. snip.trigger .. "." end, {}) ), -- It's possible to use capture-groups inside regex-triggers. s( { trig = "b(%d)", regTrig = true }, f(function(_, snip) return "Captured Text: " .. snip.captures[1] .. "." end, {}) ), s({ trig = "c(%d+)", regTrig = true }, { t("will only expand for even numbers"), }, { condition = function(line_to_cursor, matched_trigger, captures) return tonumber(captures[1]) % 2 == 0 end, }), -- Use a function to execute any shell command and print its text. s("bash", f(bash, {}, "ls")), -- Short version for applying String transformations using function nodes. s("transform", { i(1, "initial text"), t({ "", "" }), -- lambda nodes accept an l._1,2,3,4,5, which in turn accept any string transformations. -- This list will be applied in order to the first node given in the second argument. l(l._1:match("[^i]*$"):gsub("i", "o"):gsub(" ", "_"):upper(), 1), }), s("transform2", { i(1, "initial text"), t("::"), i(2, "replacement for e"), t({ "", "" }), -- Lambdas can also apply transforms USING the text of other nodes: l(l._1:gsub("e", l._2), { 1, 2 }), }), s({ trig = "trafo(%d+)", regTrig = true }, { -- env-variables and captures can also be used: l(l.CAPTURE1:gsub("1", l.TM_FILENAME), {}), }), -- Set store_selection_keys = "<Tab>" (for example) in your -- luasnip.config.setup() call to access TM_SELECTED_TEXT. In -- this case, select a URL, hit Tab, then expand this snippet. s("link_url", { t('<a href="'), f(function(_, snip) return snip.env.TM_SELECTED_TEXT[1] or {} end, {}), t('">'), i(1), t("</a>"), i(0), }), -- Shorthand for repeating the text in a given node. s("repeat", { i(1, "text"), t({ "", "" }), r(1) }), -- Directly insert the ouput from a function evaluated at runtime. s("part", p(os.date, "%Y")), -- use matchNodes to insert text based on a pattern/function/lambda-evaluation. s("mat", { i(1, { "sample_text" }), t(": "), m(1, "%d", "contains a number", "no number :("), }), -- The inserted text defaults to the first capture group/the entire -- match if there are none s("mat2", { i(1, { "sample_text" }), t(": "), m(1, "[abc][abc][abc]"), }), -- It is even possible to apply gsubs' or other transformations -- before matching. s("mat3", { i(1, { "sample_text" }), t(": "), m( 1, l._1:gsub("[123]", ""):match("%d"), "contains a number that isn't 1, 2 or 3!" ), }), -- `match` also accepts a function, which in turn accepts a string -- (text in node, \n-concatted) and returns any non-nil value to match. -- If that value is a string, it is used for the default-inserted text. s("mat4", { i(1, { "sample_text" }), t(": "), m(1, function(text) return (#text % 2 == 0 and text) or nil end), }), -- The nonempty-node inserts text depending on whether the arg-node is -- empty. s("nempty", { i(1, "sample_text"), n(1, "i(1) is not empty!"), }), -- dynamic lambdas work exactly like regular lambdas, except that they -- don't return a textNode, but a dynamicNode containing one insertNode. -- This makes it easier to dynamically set preset-text for insertNodes. s("dl1", { i(1, "sample_text"), t({ ":", "" }), dl(2, l._1, 1), }), -- Obviously, it's also possible to apply transformations, just like lambdas. s("dl2", { i(1, "sample_text"), i(2, "sample_text_2"), t({ "", "" }), dl(3, l._1:gsub("\n", " linebreak ") .. l._2, { 1, 2 }), }), -- Alternative printf-like notation for defining snippets. It uses format -- string with placeholders similar to the ones used with Python's .format(). s( "fmt1", fmt("To {title} {} {}.", { i(2, "Name"), i(3, "Surname"), title = c(1, { t("Mr."), t("Ms.") }), }) ), -- To escape delimiters use double them, e.g. `{}` -> `{{}}`. -- Multi-line format strings by default have empty first/last line removed. -- Indent common to all lines is also removed. Use the third `opts` argument -- to control this behaviour. s( "fmt2", fmt( [[ foo({1}, {3}) {{ return {2} * {4} }} ]], { i(1, "x"), r(1), i(2, "y"), r(2), } ) ), -- Empty placeholders are numbered automatically starting from 1 or the last -- value of a numbered placeholder. Named placeholders do not affect numbering. s( "fmt3", fmt("{} {a} {} {1} {}", { t("1"), t("2"), a = t("A"), }) ), -- The delimiters can be changed from the default `{}` to something else. s("fmt4", fmt("foo() { return []; }", i(1, "x"), { delimiters = "[]" })), -- `fmta` is a convenient wrapper that uses `<>` instead of `{}`. s("fmt5", fmta("foo() { return <>; }", i(1, "x"))), -- By default all args must be used. Use strict=false to disable the check s( "fmt6", fmt("use {} only", { t("this"), t("not this") }, { strict = false }) ), }, java = { -- Very long example for a java class. s("fn", { d(6, jdocsnip, { 2, 4, 5 }), t({ "", "" }), c(1, { t("public "), t("private "), }), c(2, { t("void"), t("String"), t("char"), t("int"), t("double"), t("boolean"), i(nil, ""), }), t(" "), i(3, "myFunc"), t("("), i(4), t(")"), c(5, { t(""), sn(nil, { t({ "", " throws " }), i(1), }), }), t({ " {", "\t" }), i(0), t({ "", "}" }), }), }, tex = { -- rec_ls is self-referencing. That makes this snippet 'infinite' eg. have as many -- \item as necessary by utilizing a choiceNode. s("ls", { t({ "\\begin{itemize}", "\t\\item " }), i(1), d(2, rec_ls, {}), t({ "", "\\end{itemize}" }), }), }, } -- autotriggered snippets have to be defined in a separate table, luasnip.autosnippets. ls.autosnippets = { all = { s("autotrigger", { t("autosnippet"), }), }, } -- in a lua file: search lua-, then c-, then all-snippets. ls.filetype_extend("lua", { "c" }) -- in a cpp file: search c-snippets, then all-snippets only (no cpp-snippets!!). ls.filetype_set("cpp", { "c" }) --[[ -- 除了定义你自己的代码片段,你还可以从“类似 vscode”的包中加载代码片段 -- 在 json 文件中公开片段,例如 <https://github.com/rafamadriz/friendly-snippets>. -- 请注意,这将扩展 `ls.snippets`,因此您需要在您自己的代码片段之后执行此操作,或者您 -- 需要自己扩展表格而不是设置一个新表格。 ]] --require("luasnip/loaders/from_vscode").load({ include = { "javascript" } }) -- Load only python snippets require("luasnip/loaders/from_vscode").load() -- Load only python snippets -- The directories will have to be structured like eg. <https://github.com/rafamadriz/friendly-snippets> (include -- a similar `package.json`) --require("luasnip/loaders/from_vscode").load({ paths = { "./my-snippets" } }) -- Load snippets from my-snippets folder --require("luasnip/loaders/from_vscode").load({ paths = { "/Users/itkey/Documents/my-snippets/" } }) -- Load snippets from my-snippets folder -- You can also use lazy loading so you only get in memory snippets of languages you use --require("luasnip/loaders/from_vscode").lazy_load() -- You can pass { paths = "./my-snippets/"} as well

nvim-lsp-installer打造 python IDE#

安装#

Copy
use 'kabouzeid/nvim-lspinstall'

配置#

Copy
nvim ~/.config/nvim/after/plugin/nvim-lspinstall.lua require'lspinstall'.setup() -- important local servers = require'lspinstall'.installed_servers() for _, server in pairs(servers) do require'lspconfig'[server].setup{} end

使用方式#

  • LspInstall to install/update the language server for (e.g. :LspInstall python).
  • LspUninstall to uninstall the language server for .
  • require'lspinstall'.setup() to make configs of installed servers available for require’lspconfig’..setup{}.
  • :LspInstall python

关键字提示

image-20220107005144835

代码提示

image-20220107005238858

语法检查

image-20220107005339113

Lsp UI美化#

安装#

Copy
-- 自动为尚不支持 Neovim 0.5 内置 lsp 客户端的配色方案创建缺少的 LSP 诊断突出显示组。 use "folke/lsp-colors.nvim" -- 基于neovim 内置lsp 的轻量级lsp 插件,具有高性能UI。非常酷 use 'rinx/lspsaga.nvim'

配置#

Copy
nvim ~/.config/nvim/after/plugin/lsp-colors.rc.vim if !exists('#LspColors') | finish | endif lua << EOF require("lsp-colors").setup({ Error = "#db4b4b", Warning = "#e0af68", Information = "#0db9d7", Hint = "#10B981" }) EOF

lsp-colors.nvim配置#

Copy
nvim ~/.config/nvim/after/plugin/lspsaga.rc.vim if !exists('g:loaded_lspsaga') | finish | endif lua << EOF local saga = require 'lspsaga' --[[ saga.init_lsp_saga { error_sign = '', warn_sign = '', hint_sign = '', infor_sign = '', border_style = "round", } ]]-- saga.init_lsp_saga { error_sign = ' ', warn_sign = ' ', hint_sign = '', infor_sign = ' ', border_style = "round", } EOF "nnoremap <silent> <C-j> <Cmd>Lspsaga diagnostic_jump_next<CR> nnoremap <silent> <leader>j <Cmd>Lspsaga diagnostic_jump_next<CR> nnoremap <silent> <Cmd>Lspsaga diagnostic_jump_next<CR> nnoremap <silent>K <Cmd>Lspsaga hover_doc<CR> nnoremap <silent> <C-f> <cmd>lua require('lspsaga.action').smart_scroll_with_saga(1)<CR> " scroll up hover doc nnoremap <silent> <C-b> <cmd>lua require('lspsaga.action').smart_scroll_with_saga(-1)<CR> "inoremap <silent> <C-k> <Cmd>Lspsaga signature_help<CR> nnoremap <silent> <leader>k <Cmd>Lspsaga signature_help<CR> nnoremap <silent> gh <Cmd>Lspsaga lsp_finder<CR> " code action " 智能处理,使用IDEA Alt+Enter 同样按键 nnoremap <silent><M-Enter> <cmd>lua require('lspsaga.codeaction').code_action()<CR> nnoremap <silent><leader>ca <cmd>lua require('lspsaga.codeaction').code_action()<CR> vnoremap <silent><leader>ca :<C-U>lua require('lspsaga.codeaction').range_code_action()<CR> " 重命名 感觉没有lsp自带的功能好用,因为名称要从头输入 nnoremap <silent><leader>rn <cmd>lua require('lspsaga.rename').rename()<CR> "预览定义 nnoremap <silent> <leader>gd <cmd>lua require'lspsaga.provider'.preview_definition()<CR> "Jump Diagnostic and Show Diagnostics nnoremap <silent> <leader>cd <cmd>lua require'lspsaga.diagnostic'.show_line_diagnostics()<CR> " 打开终端 nnoremap <silent> <A-d> :Lspsaga open_floaterm<CR> " 关闭终端 tnoremap <silent> <A-d> <C-\><C-n>:Lspsaga close_floaterm<CR>

autopairs自动闭合#

Copy
-- 显示css的颜色代码的颜色 use "ap/vim-css-color" -- 符号自动匹配,比如:输入(自动闭合) use "windwp/nvim-autopairs" -- 需要加第三方支持 brew install ripgrep

配置#

Copy
nvim ~/.config/nvim/after/plugin/autopairs.rc.lua local status, autopairs = pcall(require, "nvim-autopairs") if (not status) then return end autopairs.setup({ disable_filetype = { "TelescopePrompt" , "vim" }, })

image-20220107005843752

telescope文件搜索#

安装#

Copy
-- 文件搜索 预览 等 use { "nvim-telescope/telescope.nvim", requires = { "nvim-lua/plenary.nvim", "kyazdani42/nvim-web-devicons" } } -- 加速文件搜索速度,如果安装失败需要到插件目录执行make命令手动编译 -- 用了这个插件以后,貌似更快了(感觉输入更跟手了,可能是心理作用)。但是对于我的小项目感受不是很明显 use {"nvim-telescope/telescope-fzf-native.nvim", run = "make"}

配置#

Copy
nvim ~/.config/nvim/after/plugin/telescope.lua local status, actions = pcall(require, "telescope.actions") if (not status) then return end --local actions = require('telescope.actions') -- Global remapping ------------------------------ require("telescope").setup { defaults = { mappings = { n = { ["q"] = actions.close, ["l"] = actions.file_edit } }, file_ignore_patterns = {"./node_modules"} }, extensions = { fzf = { fuzzy = true, -- false will only do exact matching override_generic_sorter = true, -- override the generic sorter override_file_sorter = true, -- override the file sorter case_mode = "smart_case" -- or "ignore_case" or "respect_case" -- the default case_mode is "smart_case" } } } -- To get fzf loaded and working with telescope, you need to call -- load_extension, somewhere after setup function: require("telescope").load_extension("fzf") --按键设置 vim.api.nvim_set_keymap("n", "<leader>ff", [[<cmd>lua require('telescope.builtin').find_files()<cr>]], {}) vim.api.nvim_set_keymap("n", "<leader>fg", [[<cmd>lua require('telescope.builtin').live_grep()<cr>]], {}) vim.api.nvim_set_keymap("n", "<leader>fb", [[<cmd>lua require('telescope.builtin').buffers()<cr>]], {}) vim.api.nvim_set_keymap("n", "<leader>fh", [[<cmd>lua require('telescope.builtin').help_tags()<cr>]], {}) vim.api.nvim_set_keymap("n", "<leader>sf", [[<cmd>lua require('telescope.builtin').file_browser()<cr>]], {}) vim.api.nvim_set_keymap("n", "<leader>/", [[<cmd>lua require'telescope.builtin'.current_buffer_fuzzy_find{}<CR>]], {})

使用方式#

使用快捷键 ts

image-20220107010224184

nvim-treesitter语法高亮#

安装#

Copy
--语法高亮 use { 'nvim-treesitter/nvim-treesitter', run = ':TSUpdate' }

配置#

Copy
nvim ~/.config/nvim/after/plugin/nvim-treesitter.lua local status, treesitter = pcall(require, "nvim-treesitter.configs") if (not status) then return end treesitter.setup { highlight = { enable = true, disable = {} }, indent = { enable = false, disable = {} }, ensure_installed = { "tsx", "toml", "fish", "php", "json", "yaml", "swift", "html", "scss" } } local parser_config = require "nvim-treesitter.parsers".get_parser_configs() parser_config.tsx.used_by = {"javascript", "typescript.tsx"}

使用#

Copy
:TSInstall <language_to_install>

rainbow彩虹括号#

安装#

Copy
--彩虹括号 use 'luochen1990/rainbow'

配置#

Copy
nvim ~/.config/nvim/after/plugin/rainbow.rc.vim let g:rainbow_active = 1 "0 if you want to enable it later via :RainbowToggle let g:rainbow_conf = { \ 'guifgs': ['royalblue3', 'darkorange3', 'seagreen3', 'firebrick'], \ 'ctermfgs': ['lightblue', 'lightyellow', 'lightcyan', 'lightmagenta'], \ 'operators': '_,_', \ 'parentheses': ['start=/(/ end=/)/ fold', 'start=/\[/ end=/\]/ fold', 'start=/{/ end=/}/ fold'], \ 'separately': { \ '*': {}, \ 'tex': { \ 'parentheses': ['start=/(/ end=/)/', 'start=/\[/ end=/\]/'], \ }, \ 'lisp': { \ 'guifgs': ['royalblue3', 'darkorange3', 'seagreen3', 'firebrick', 'darkorchid3'], \ }, \ 'vim': { \ 'parentheses': ['start=/(/ end=/)/', 'start=/\[/ end=/\]/', 'start=/{/ end=/}/ fold', 'start=/(/ end=/)/ containedin=vimFuncBody', 'start=/\[/ end=/\]/ containedin=vimFuncBody', 'start=/{/ end=/}/ fold containedin=vimFuncBody'], \ }, \ 'html': { \ 'parentheses': ['start=/\v\<((area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)[ >])@!\z([-_:a-zA-Z0-9]+)(\s+[-_:a-zA-Z0-9]+(\=("[^"]*"|'."'".'[^'."'".']*'."'".'|[^ '."'".'"><=`]*))?)*\>/ end=#</\z1># fold'], \ }, \ 'css': 0, \ } \}

加载优先级#

Copy
# 因为这个插件的配置需要比较高的加载级别,所以在init.vim文件中增加下面一行: vim ~/.config/nvim/init.vim source ~/.config/nvim/after/plugin/rainbow.rc.vim

image-20220107010902615

kommentary注释插件#

安装#

Copy
--注释插件 use "b3nj5m1n/kommentary"

配置#

Copy
nvim ~/.config/nvim/after/plugin/kommentary.lua treesitter.setup { context_commentstring = { enable = true, enable_autocmd = false, context_commentstring = { enable = true, config = { javascript = { __default = "// %s", jsx_element = "{/* %s */}", jsx_fragment = "{/* %s */}", jsx_attribute = "// %s", comment = "// %s" } } } } }

使用方式#

gcc 快速注释一行

image-20220107012724165

gc + 数字(注释几行) + 方向(向上/向下注释)

image-20220107012809728

indent缩进线插件#

安装#

Copy
-- indent缩进线 use "lukas-reineke/indent-blankline.nvim"

配置#

Copy
nvim ~/.config/nvim/after/plugin/indent-blakline.lua -- vim.g.indent_blankline_char = '│' vim.wo.colorcolumn = "99999" vim.g.indent_blankline_char = "┊" vim.g.indent_blankline_show_first_indent_level = true vim.g.indent_blankline_filetype_exclude = { -- 'startify', "dashboard", -- 'dotooagenda', -- 'log', -- 'fugitive', -- 'gitcommit', -- 'packer', -- 'vimwiki', -- 'markdown', -- 'json', -- 'txt', -- 'vista', -- 'help', -- 'todoist', -- 'NvimTree', -- 'peekaboo', -- 'git', -- 'TelescopePrompt', -- 'undotree', -- 'flutterToolsOutline', "" -- for all buffers without a file type } vim.g.indent_blankline_buftype_exclude = {"terminal", "nofile"} vim.g.indent_blankline_show_trailing_blankline_indent = false vim.g.indent_blankline_show_current_context = true vim.g.indent_blankline_context_patterns = { "class", "function", "method", "block", "list_literal", "selector", "^if", "^table", "if_statement", "while", "for" } -- because lazy load indent-blankline so need readd this autocmd vim.cmd("autocmd CursorMoved * IndentBlanklineRefresh")

image-20220107013807587

nvim-tre文件目录树#

安装#

Copy
-- 文件管理树 use { 'kyazdani42/nvim-tree.lua', requires = { 'kyazdani42/nvim-web-devicons', -- optional, for file icon }, config = function() require'nvim-tree'.setup {} end }

配置#

Copy
nvim ~/.config/nvim/after/plugin/tree.lua vim.g.nvim_tree_indent_markers = 1 vim.g.nvim_tree_allow_resize = 1 vim.g.nvim_tree_icons = { default = "", symlink = "" } vim.g.nvim_tree_show_icons = { git = 0, folders = 1, files = 1 } vim.g.nvim_tree_gitignore = 0 vim.g.nvim_tree_git_gl = 0 local tree_cb = require("nvim-tree.config").nvim_tree_callback require("nvim-tree").setup { auto_close = true, update_focused_file = { update_cwd = true }, view = { mappings = { list = { {key = "l", cb = tree_cb("edit")} } } } }

image-20220107015049637

快捷键#

快捷键 操作
回车键/o(两者作用相同) 打开文件/目录
Ctrl - ] 切换到光标所在目录
a 新建文件
r 重命名文件
x 剪切文件
c 复制文件
p 粘贴文件
d 删除文件
H 是否显示隐藏文件
Tab 预览文件
Ctrk-v 垂直拆分中打开文件
Ctrl-x 水平拆分中打开文件

undotree 历史修改记录#

安装#

Copy
-- 显示文件修改记录 use "mbbill/undotree"

配置#

Copy
mkdir -p ~/.config/nvim/.undodir nvim ~/.config/nvim/after/plugin/undotree.vim if has("persistent_undo") let target_path = expand('~/.config/nvim/.undodir') " create the directory and any parent directories " if the location does not exist. if !isdirectory(target_path) call mkdir(target_path, "p", 0700) endif let &undodir=target_path set undofile endif

使用方式#

进入需要恢复的序号中输入回车或者 u 即可恢复

image-20220107020708910

vimspector代码调试#

安装#

Copy
use "puremourning/vimspector"

配置#

Copy
vim ~/.config/nvim/after/plugin/vimspector.vim let g:vimspector_enable_mappings = 'HUMAN' function! s:read_template_into_buffer(template) " has to be a function to avoid the extra space fzf#run insers otherwise execute '0r ~/.config/nvim/sample_vimspector_json/'.a:template endfunction command! -bang -nargs=* LoadVimSpectorJsonTemplate call fzf#run({ \ 'source': 'ls -1 ~/.config/nvim/sample_vimspector_json', \ 'down': 20, \ 'sink': function('<sid>read_template_into_buffer') \ }) noremap <leader>vs :tabe .vimspector.json<CR>:LoadVimSpectorJsonTemplate<CR> sign define vimspectorBP text=🛑 texthl=Normal sign define vimspectorBPDisabled text=🚫 texthl=Normal sign define vimspectorPC text=👉 texthl=SpellBad mkdir ~/.config/nvim/sample_vimspector_json nvim ~/.config/nvim/sample_vimspector_json/python.json { "adapters": { "debugpy": { "command": [ "python", "-m", "debugpy.adapter" ], "name": "debugpy", "configuration": { "python": "python" } } }, "configurations": { "run - debugpy": { "adapter": "debugpy", "configuration": { "request": "launch", "type": "python", "cwd": "${workspaceRoot}", "program": "${file}", "stopOnEntry": true, "console": "integratedTerminal" }, "breakpoints": { "exception": { "raised": "N", "uncaught": "" } } } } }

使用方式#

第一步使用快捷键 空格 + vs 选择配置文件并保存

每次只需要第一次进行调试需要选择保存文件之后无需再次选择了

image-20220107021311391

第二步使用 F9 打断点

image-20220107021721902

使用 F5启动调试

image-20220107021759977

F10 进行下一步调试

image-20220107021906931

F4 重启调试

image-20220107022049526

pydocstring函数参数注释#

安装#

Copy
-- 代码函数注释插件 use "heavenshell/vim-pydocstring" # 需要第三方支持 pip3 install doq

配置#

Copy
nvim ~/.config/nvim/after/plugin/pydocstring.vim let g:pydocstring_templates_path = '~/.config/nvim/doc' # 选择 doq 路径 let g:pydocstring_doq_path = "/opt/homebrew/bin/doq" mkdir -p ~/.config/nvim/doc vim ~/.config/nvim/doc/def.txt """ Description of {{ name }}: {% if params %} {%- for p in params %} param {{ p.argument }}{% if p.annotation %}({{ p.annotation }}): {% endif -%}{% if p.default %} default to {{ p.default }}{% endif -%} {% endfor %} {% endif -%} {% if return_type %} return {{ return_type }}: {% endif -%} {% if yields %} Yields: {%- for y in yields %} {{ y }}: {%- endfor %} {% endif -%} {% if exceptions %} Raises: {%- for e in exceptions %} {{ e }}: {%- endfor %} {% endif -%} """

使用方式#

光标位于函数上使用快捷键 ctrl + _

image-20220107022901706

SimpylFold代码折叠#

安装#

Copy
-- 代码折叠插件 use "tmhedberg/SimpylFold" use "Konfekt/FastFold" use "zhimsel/vim-stay"

配置#

Copy
vim ~/.config/nvim/vimrc.vim set foldmethod=indent set foldlevel=99 set foldenable set viewoptions=cursor,folds,slash,unix let g:fastfold_savehook = 0 let g:SimpylFold_docstring_preview=1

使用方式#

使用 za 即可快速进行折叠以及展开

image-20220107023208045

formatter代码格式化#

安装#

Copy
use "mhartington/formatter.nvim"

配置#

Copy
nvim ~/.config/nvim/after/plugin/formatter.lua local status, formatter = pcall(require, "formatter") if (not status) then return end formatter.setup( { filetype = { lua = { -- luafmt function() return { exe = "luafmt", args = {"--indent-count", 2, "--stdin"}, stdin = true } end }, python = { function() return { exe = "python3 -m autopep8", args = { "--in-place --aggressive --aggressive", vim.fn.fnameescape(vim.api.nvim_buf_get_name(0)) }, stdin = false } end }, sql = { -- sqlformat -- 安装方法:pip3 install --upgrade sqlparse function() return { exe = "sqlformat", -- upper|lower args = {"-k", "lower", "-i", "lower", "-r", "-"}, stdin = true } end } } } ) --配置保存文件自动格式化代码 vim.api.nvim_exec( [[ augroup FormatAutogroup autocmd! autocmd BufWritePost *.rs,*.lua,*.py FormatWrite augroup END ]], true )

使用方式#

使用快捷键 ctrl + s 即可

vista 函数展示#

安装#

Copy
use "liuchengxu/vista.vim" # 安装第三方依赖 brew install universal-ctags

配置#

Copy
nvim ~/.config/nvim/after/plugin/vista.vim function! NearestMethodOrFunction() abort return get(b:, 'vista_nearest_method_or_function', '') endfunction set statusline+=%{NearestMethodOrFunction()} " By default vista.vim never run if you don't call it explicitly. " " If you want to show the nearest function in your statusline automatically, " you can add the following line to your vimrc autocmd VimEnter * call vista#RunForNearestMethodOrFunction() " How each level is indented and what to prepend. " This could make the display more compact or more spacious. " e.g., more compact: ["▸ ", ""] " Note: this option only works for the kind renderer, not the tree renderer. let g:vista_icon_indent = ["╰─▸ ", "├─▸ "] " Executive used when opening vista sidebar without specifying it. " See all the avaliable executives via `:echo g:vista#executives`. let g:vista_default_executive = 'ctags' " Set the executive for some filetypes explicitly. Use the explicit executive " instead of the default one for these filetypes when using `:Vista` without " specifying the executive. let g:vista_executive_for = { \ 'cpp': 'vim_lsp', \ 'php': 'vim_lsp', \ } " Declare the command including the executable and options used to generate ctags output " for some certain filetypes.The file path will be appened to your custom command. " For example: let g:vista_ctags_cmd = { \ 'haskell': 'hasktags -x -o - -c', \ } " To enable fzf's preview window set g:vista_fzf_preview. " The elements of g:vista_fzf_preview will be passed as arguments to fzf#vim#with_preview() " For example: let g:vista_fzf_preview = ['right:50%'] " Ensure you have installed some decent font to show these pretty symbols, then you can enable icon for the kind. let g:vista#renderer#enable_icon = 1 " The default icons can't be suitable for all the filetypes, you can extend it as you wish. let g:vista#renderer#icons = { \ "function": "\uf794", \ "variable": "\uf71b", \ }

image-20220107024208603

jedi源码跳转#

安装#

Copy
-- 代码跳转 use "davidhalter/jedi-vim" # 安装第三方插件 pip3 install jedi

配置#

Copy
nvim ~/.config/nvim/after/plugin/jedi.vim " 代码跳转 " disable autocompletion, cause we use deoplete for completion let g:jedi#completions_enabled = 0 " open the go-to function in split, not another buffer let g:jedi#use_splits_not_buffers = "right"

使用方式#

使用空格+d 跳转到源码内部

image-20220107032357509

空格 + r 修改函数名称

image-20220107032459239

K (大写)查看函数使用文档

image-20220107032612893

symbols-outline展示函数变量#

安装#

Copy
use "simrat39/symbols-outline.nvim"

**image-20220107024605604#

nvim-repl代码 repl#

安装#

Copy
-- 代码 repl 插件 use "tpope/vim-repeat" use "pappasam/nvim-repl"

配置#

Copy
nvim ~/.config/nvim/after/plugin/nvim-repl.vim let g:repl_filetype_commands = { \ 'python': 'python3', \ } let g:repl_split = 'right'

使用方式#

使用快捷键 re 开启 repl

image-20220107025053542

使用空格 + e 发送代码进入右边区域

image-20220107025128233

代码块发送先选中代码块在使用空格+ e

image-20220107025305179

toggleterm开启终端#

安装#

Copy
-- 终端调用插件 use "akinsho/nvim-toggleterm.lua"

配置#

Copy
nvim ~/.config/nvim/after/plugin/terminal.lua require("toggleterm").setup { -- size can be a number or function which is passed the current terminal size = 15, open_mapping = [[<c-\>]], hide_numbers = true, -- hide the number column in toggleterm buffers shade_filetypes = {}, shade_terminals = true, shading_factor = "<number>", -- the degree by which to darken to terminal colour, default: 1 for dark backgrounds, 3 for light start_in_insert = true, insert_mappings = true, -- whether or not the open mapping applies in insert mode persist_size = true, direction = "horizontal", close_on_exit = true, -- close the terminal window when the process exits shell = "/bin/zsh", -- change the default shell -- This field is only relevant if direction is set to 'float' float_opts = { -- The border key is *almost* the same as 'nvim_win_open' -- see :h nvim_win_open for details on borders however -- the 'curved' border is a custom border type -- not natively supported but implemented in this plugin. border = "single", height = 100, winblend = 3, highlights = { border = "Normal", background = "Normal" } } } function _G.set_terminal_keymaps() local opts = {noremap = true} vim.api.nvim_buf_set_keymap(0, "t", "<esc>", [[<C-\><C-n>]], opts) vim.api.nvim_buf_set_keymap(0, "t", "jk", [[<C-\><C-n>]], opts) vim.api.nvim_buf_set_keymap(0, "t", "<C-h>", [[<C-\><C-n><C-W>h]], opts) vim.api.nvim_buf_set_keymap(0, "t", "<C-j>", [[<C-\><C-n><C-W>j]], opts) vim.api.nvim_buf_set_keymap(0, "t", "<C-k>", [[<C-\><C-n><C-W>k]], opts) vim.api.nvim_buf_set_keymap(0, "t", "<C-l>", [[<C-\><C-n><C-W>l]], opts) end -- if you only want these mappings for toggle term use term://*toggleterm#* instead vim.cmd("autocmd! TermOpen term://* lua set_terminal_keymaps()")

使用方式#

使用快捷键 ctrl + t 调用终端

image-20220107030310782

vim-surround符号匹配#

安装#

Copy
-- 符号匹配 use "tpope/vim-surround" -- 快速选中 use "gcmt/wildfire.vim"

使用方式#

使用回车快速选中

image-20220107030615346

大写 S 加上需要添加的符号

image-20220107030649255

cs 进行修改 光标放在需要修改的符号上使用 cs当前符号+修改符号

image-20220107030749323

switch快速转换#

安装#

Copy
use "AndrewRadev/switch.vim"

使用方式#

光标放在当前需要修改的变量使用 gs

image-20220107031123532

snippets代码片段#

安装#

Copy
-- 代码块引擎 use "SirVer/ultisnips" -- 代码块片段 use "honza/vim-snippets"

配置#

Copy
nvim ~/.config/nvim/after/plugin/ultiSnip.vim let g:UltiSnipsExpandTrigger="<c-e>" let g:UltiSnipsJumpForwardTrigger="<tab>" let g:UltiSnipsJumpBackwardTrigger="<S-tab>"

使用方式#

使用快捷键ctrl + e 进行代码片段补全

image-20220107031412063

使用tab跳转到需要修改的地方 即上图光标闪烁处

image-20220107031707685

markdown预览#

Copy
-- markdwon预览插件 use "suan/vim-instant-markdown"

函数快捷键映射#

Copy
nvim ~/.config/nvim/maps.vim " 执行vistar函数展示功能 nnoremap <silent><nowait> tb :<C-u>Vista!!<cr> " 查看文件修改历史 nnoremap ub :UndotreeToggle<CR> " 开启终端 nnoremap <C-t> :ToggleTerm<CR> " 快速查看函数变量 nnoremap so :SymbolsOutline<CR> " 开启文件搜索功能 nmap ts :Telescope<CR> " 目录树 nnoremap nt :NvimTreeToggle<CR> nnoremap nr :NvimTreeRefresh<CR> nnoremap nf :NvimTreeFindFile<CR> " 快速定位错误代码 " use <C-N> and <C-P> for next/prev. nnoremap <silent> hn <cmd>QNext<CR> nnoremap <silent> hp <cmd>QPrev<CR> " toggle the quickfix open/closed without jumping to it nnoremap <silent> <leader>q <cmd>QFToggle!<CR> nnoremap <silent> <leader>l <cmd>LLToggle!<CR> " repl 进行调试 " neoremae <leader><leader>e :ReplToggle<CR> nnoremap re :ReplToggle<CR> nmap <leader>e <Plug>ReplSendLine vmap <leader>e <Plug>ReplSendVisual " 执行python代码 autocmd FileType python map <buffer> <C-r> :w<CR>:exec '!python3.9' shellescape(@%, 1)<CR> autocmd FileType python imap <buffer> <C-r> <esc>:w<CR>:exec '!python3.9' shellescape(@%, 1)<CR> " 代码进行注释 nmap <silent> <C-_> <Plug>(pydocstring)

快捷键说明#

快捷键 操作
ss 水平分割
sv 垂直分割
s + h/j/k/l 多窗口进行移动
Alt + 上下左右方向键 调节窗口大小
Ctrl + h/j/k/l 插入模式进行方向移动
L 快速到行尾
H 快速到行首
Ctrl + j/k 普通模式一次性跳转五行
Ctrl + s 保存
Ctrl + a 全选
R 加载配置
tab 进入下一个标签r栏
Shitf+tab 进入上一个标签栏
Ctrl + r 一键执行 python 代码

init 初始化配置#

Copy
" 配置文件导入 "{{{ " --------------------------------------------------------------------- " 基础使用习惯配置 runtime ./vimrc.vim " 插件管理 "./lua/plugins.lua lua require('plugins') " 按键映射 runtime ./maps.vim " 加载配置文件 source ~/.config/nvim/after/plugin/vimspector.vim source ~/.config/nvim/after/plugin/defx-icons.rc.vim source ~/.config/nvim/after/plugin/rainbow.rc.vim source ~/.config/nvim/after/plugin/ultiSnip.vim

PS:最终配置

参考文章

posted @   SR丶  阅读(10356)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
CONTENTS