编辑器不是身份认同,是工具。这篇文章覆盖两条路:终端编辑器(Vim/Neovim)和主流 GUI 编辑器。两条路都成立,用 VSCode 做日常开发、用 Vim 处理服务器上的紧急修改,是完全正常的组合。

1. 两种场景,两种工具

终端编辑器的场景:SSH 进生产服务器、进容器修改配置、连接没有 GUI 的远程机器、调试 CI 脚本——这些场景里没有图形界面,Vim 或 Neovim 往往是唯一选项。即便日常用 VSCode,也值得有能在裸机终端里稳定工作的能力。

GUI 编辑器的场景:本地项目开发、代码重构、可视化调试、团队协作——VSCode、JetBrains、Zed 在这里提供了更完整的体验。它们有成熟的插件生态,十年后也仍然是有效的选择。

不需要在”只用 Vim”和”只用 GUI”之间做非此即彼的选择。真正的问题是:在哪个场景下,你用的工具能让你最高效地完成任务。

2. Vim 的编辑语言

Vim 最反直觉的地方不是快捷键多,而是它把”移动”和”输入”拆成了不同的模式。一旦接受这个设计,Vim 的命令就不再是孤立快捷键,而像一套可组合的编辑语言

三种基本模式

  • Normal(普通模式):打开 Vim 就在这里。用于移动、删除、复制、执行命令。大多数操作都在这里完成。
  • Insert(插入模式):按 iao 进入,这时输入的字符才会出现在文件里。按 Esc 回到 Normal。
  • Visual(可视模式):按 v 进入字符选择,V 进入行选择,Ctrl+v 进入块选择。选中后可以对选区执行操作。

动词 + 介词 + 名词

Vim 的编辑语法是:动词 → 介词 → 名词,三者组合成一个完整的编辑动作。

动词

d  # 删除 delete
c  # 修改 change(删除后进入插入模式)
y  # 复制 yank
v  # 选取 visual select
r  # 替换单个字符 replace

名词(文本对象):

w  # 单词 word
s  # 句子 sentence
p  # 段落 paragraph
t  # HTML 标签 tag

介词(范围限定):

i  # 内部 inside(不含边界)
a  # 环绕 around(含边界)
t  # 到某字符之前 to
f  # 到某字符之上 forward

数词(可选):修饰动词或名词,表示数量:

d2w   # 删除两个单词
c3w   # 修改三个单词
3x    # 删除三个字符

组合示例

ciw   # change inside word  — 修改光标所在单词(不含空格)
caw   # change around word  — 修改光标所在单词(含周围空格)
dip   # delete inside paragraph — 删除当前段落(不含空行)
vis   # visual select inside sentence — 选取当前句子
dtx   # delete to x  — 删除到字符 x(不含 x)
dfx   # delete forward x — 删除到字符 x(含 x)

读懂这个结构,比死记更多命令更重要。ciw 不是三个随机按键,而是”修改单词内部”的自然语言缩写。

基本移动

动作命令说明
字符移动h / j / k / l左 / 下 / 上 / 右
单词跳跃w / b / e下一词首 / 上一词首 / 词尾
行内定位0 / ^ / $行首 / 非空行首 / 行尾
字符查找f{c} / t{c}跳到字符 c / 跳到字符 c 前
文件跳转gg / G / :n文件头 / 文件尾 / 第 n 行
段落跳转{ / }上一空行 / 下一空行
翻页Ctrl+u / Ctrl+d上半页 / 下半页

3. 最小生存指南

在陌生服务器上要完成一次文本修改,需要的最小闭包:

进入和退出编辑模式

命令说明
i在光标前插入
a在光标后插入
o在当前行下方新建行并插入
O在当前行上方新建行并插入
I在行首插入
A在行尾插入
Esc / Ctrl+[退出插入模式,回到 Normal

保存和退出

命令说明
:w保存
:q关闭(无修改时)
:wq / :x / ZZ保存并退出
:q! / ZQ放弃修改强制退出
:wqa保存并退出所有文件

基本操作

命令说明
u撤销(可多次)
Ctrl+r重做
dd删除当前行
yy复制当前行
p / P在光标后 / 前粘贴
/pattern向前搜索
n / N下一个 / 上一个匹配
r{c}替换光标处单个字符为 c
.重复上一个操作

先掌握这张表,能在服务器上完成普通编辑任务,才不会被”怎么退出 Vim”卡住。先学可逆操作u(撤销)和 :q!(放弃修改退出)让你敢于试错。

剪贴板操作

命令说明
"+y复制到系统剪贴板
"+p从系统剪贴板粘贴
"*y / "*p同上(macOS 兼容写法)

4. 配置:从裸机到够用

vimrc 基础结构

Vim 配置文件位置:

  • Linux/macOS:~/.vimrc~/.vim/vimrc
  • 查看当前路径::echo $MYVIMRC

一个够用的最小配置,按职责分段:

" --- 基础行为 ---
set nocompatible           " 关闭 Vi 兼容模式
set encoding=utf-8
set clipboard=unnamedplus  " 使用系统剪贴板
 
" --- 界面 ---
set number                 " 行号
set relativenumber         " 相对行号(方便 5j 这类跳转)
set cursorline             " 高亮当前行
syntax on
set termguicolors
 
" --- 编辑行为 ---
set tabstop=4
set shiftwidth=4
set expandtab              " Tab 转空格
set autoindent
set backspace=indent,eol,start
 
" --- 搜索 ---
set hlsearch               " 高亮匹配
set incsearch              " 增量搜索
set ignorecase             " 搜索忽略大小写
set smartcase              " 有大写字母时区分大小写
 
" --- Leader 键 ---
let mapleader = "\<Space>"
 
" --- 常用映射 ---
nnoremap <leader>w :w<CR>
nnoremap <leader>q :q<CR>
nnoremap <leader>nh :nohlsearch<CR>

插件管理:vim-plug

vim-plug 是 Vim 中最轻量的插件管理器:

curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
  https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

vimrc 中声明插件:

call plug#begin('~/.vim/plugged')
 
Plug 'preservim/nerdtree'          " 文件树
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Plug 'junegunn/fzf.vim'            " 模糊查找
Plug 'tpope/vim-fugitive'          " Git 集成
Plug 'tpope/vim-surround'          " 快速修改括号/引号
Plug 'tpope/vim-commentary'        " 快速注释(gc + 动作)
Plug 'airblade/vim-gitgutter'      " Gutter 显示 Git 变更
 
call plug#end()

启动 Vim 后执行 :PlugInstall 安装,:PlugUpdate 更新,:PlugClean 清理已移除的插件。

常用插件说明

插件能力常用入口
NERDTree文件树侧边栏:NERDTreeToggle / Ctrl+n
fzf.vim模糊搜索文件、缓冲区、内容:Files / :Buffers / :Rg
vim-fugitiveGit 操作集成:Git status / :Gcommit
vim-surround成对符号编辑ysiw" 加引号,ds" 删引号,cs"' 换引号
vim-commentary注释/取消注释gcc(当前行),gc + 动作
vim-gitgutter在行号旁显示增删改自动运行,]h / [h 跳变更

插件的原则:每个插件都应该有对应的高频场景。文件树、模糊查找、Git 标记、注释、括号编辑这五类是高价值能力;能解释使用场景才加,否则不加。

5. 宏与批量操作

这三种手段处理”重复但不值得写脚本”的文本问题:

正则替换

:%s/pattern/replacement/g   " 全文替换
:s/pattern/replacement/g    " 当前行替换
:%s/pattern/replacement/gc  " 全文替换,每次确认

常用场景:

:%s/\s\+$//g                " 删除所有行尾空白
:%s/old_name/new_name/g     " 全文重命名变量
:%s/^\s\+//g                " 删除所有行首缩进

Vim 的正则字符类:. 匹配任意字符,\d 数字,\w 字母或数字,^ 行首,$ 行尾。替换前可先用 /pattern 搜索确认匹配范围。

宏录制与回放

qa          " 开始录制宏,存入寄存器 a
...         " 执行一系列操作
q           " 停止录制
@a          " 回放寄存器 a 中的宏
10@a        " 回放 10 次
@@          " 重复上一个宏

典型用例:给多行加序号、批量修改相似结构、整理临时数据文件。录制时注意用可重复的移动(jw),而不是绝对行号。

块可视模式多行编辑

Ctrl+v 进入块可视模式,选中一列,然后:

  • I + 输入 + Esc:在每行选中列之前插入相同文本
  • d / x:删除选中列
  • r{c}:替换选中列的所有字符为 c

常用于给多行注释掉代码、对齐赋值语句等。

6. Neovim:现代化的那条路

什么时候选 Neovim

Neovim 是 Vim 的现代分支,保留了完整的模态编辑能力,同时重构了底层架构:

特性VimNeovim
配置语言VimscriptLua(一等公民)+ Vimscript 兼容
异步支持有限原生异步架构
LSP需第三方插件内置 LSP 客户端
语法高亮正则引擎Treesitter 语法树
剪贴板+clipboard 编译选项默认支持

选 Neovim 的时机:需要 LSP 补全和跳转、Treesitter 语法高亮、愿意用 Lua 维护配置、想把编辑器配置纳入 dotfiles 并在多机复用。留在 Vim 的理由:稳定、预装于几乎所有 Unix 系统,用于快速编辑或 SSH 环境足够。

最小配置入口

配置目录:~/.config/nvim/,入口文件:init.lua

目录结构

~/.config/nvim/
├── init.lua
└── lua/
    ├── options.lua
    ├── keymaps.lua
    └── plugins/
        └── init.lua

lua/options.lua

local opt = vim.opt
opt.number = true; opt.relativenumber = true
opt.tabstop = 2; opt.shiftwidth = 2; opt.expandtab = true
opt.clipboard = "unnamedplus"
opt.ignorecase = true; opt.smartcase = true
opt.cursorline = true; opt.termguicolors = true

lua/keymaps.lua

vim.g.mapleader = " "
local map = vim.keymap.set
map("n", "<leader>w", "<cmd>w<CR>", { desc = "保存" })
map("n", "<leader>q", "<cmd>bdelete<CR>", { desc = "关闭 buffer" })
map("n", "<leader>nh", "<cmd>nohlsearch<CR>", { desc = "清除高亮" })

lazy.nvim 插件管理

lazy.nvim 是当前 Neovim 社区主流的插件管理器,声明式配置,支持懒加载:

-- init.lua 顶部
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
  vim.fn.system({ "git", "clone", "--filter=blob:none",
    "https://github.com/folke/lazy.nvim.git", "--branch=stable", lazypath })
end
vim.opt.rtp:prepend(lazypath)
 
require("options")
require("keymaps")
require("lazy").setup("plugins")

lua/plugins/init.lua

return {
  -- 语法高亮
  { "nvim-treesitter/nvim-treesitter", build = ":TSUpdate",
    config = function()
      require("nvim-treesitter.configs").setup({
        ensure_installed = { "lua", "python", "javascript", "typescript" },
        highlight = { enable = true },
      })
    end },
 
  -- 模糊搜索
  { "nvim-telescope/telescope.nvim",
    dependencies = { "nvim-lua/plenary.nvim" },
    keys = {
      { "<leader>ff", "<cmd>Telescope find_files<CR>", desc = "查找文件" },
      { "<leader>fg", "<cmd>Telescope live_grep<CR>", desc = "全文搜索" },
      { "<leader>fb", "<cmd>Telescope buffers<CR>", desc = "切换 buffer" },
    }},
 
  -- 状态栏
  { "nvim-lualine/lualine.nvim",
    config = function() require("lualine").setup() end },
 
  -- Git 变更标注
  { "lewis6991/gitsigns.nvim",
    config = function() require("gitsigns").setup() end },
}

安装后执行 :Lazy 查看状态,:TSInstall python 安装语法树。

LSP 集成

LSP(Language Server Protocol)把语言分析能力从编辑器解耦出来——同一套语言服务器可以服务 VSCode、Neovim 和其他编辑器。

plugins/init.lua 中添加:

-- LSP 服务器安装管理
{ "williamboman/mason.nvim", build = ":MasonUpdate" },
{ "williamboman/mason-lspconfig.nvim",
  config = function()
    require("mason-lspconfig").setup({
      ensure_installed = { "lua_ls", "pyright", "ts_ls", "rust_analyzer" },
    })
  end },
 
-- LSP 配置
{ "neovim/nvim-lspconfig",
  config = function()
    local lspconfig = require("lspconfig")
    local map = vim.keymap.set
    local on_attach = function(_, bufnr)
      local opts = { buffer = bufnr }
      map("n", "gd", vim.lsp.buf.definition, opts)
      map("n", "gr", vim.lsp.buf.references, opts)
      map("n", "K",  vim.lsp.buf.hover, opts)
      map("n", "<leader>rn", vim.lsp.buf.rename, opts)
      map("n", "<leader>ca", vim.lsp.buf.code_action, opts)
      map("n", "[d", vim.diagnostic.goto_prev, opts)
      map("n", "]d", vim.diagnostic.goto_next, opts)
    end
    lspconfig.pyright.setup({ on_attach = on_attach })
    lspconfig.ts_ls.setup({ on_attach = on_attach })
  end },
 
-- 自动补全
{ "hrsh7th/nvim-cmp",
  dependencies = { "hrsh7th/cmp-nvim-lsp", "hrsh7th/cmp-buffer", "hrsh7th/cmp-path" },
  config = function()
    local cmp = require("cmp")
    cmp.setup({
      mapping = cmp.mapping.preset.insert({
        ["<Tab>"] = cmp.mapping.select_next_item(),
        ["<CR>"]  = cmp.mapping.confirm({ select = true }),
      }),
      sources = { { name = "nvim_lsp" }, { name = "buffer" }, { name = "path" } },
    })
  end },

配置完成后,打开对应语言文件即可获得:自动补全、跳转定义(gd)、查找引用(gr)、Hover 文档(K)、安全重命名(<leader>rn)、实时诊断([d / ]d)。

参考配置

  • kickstart.nvim:单文件,带详细注释,适合理解 Neovim 配置结构
  • LazyVim:功能完整的发行版,了解约定但谨慎整套采用

7. 主流 GUI 编辑器

Vim 处理服务器编辑和终端场景,GUI 编辑器处理本地项目开发——它们解决不同问题,不必非此即彼。

VSCode:市场份额最大,插件生态最广(语言支持、调试、容器、AI 辅助一应俱全),微软维护,活跃度高。它是大多数人日常开发的合理默认选项,配合 Vim 键位插件(VSCodeVim / VSCode Neovim)可以同时保留模态编辑体验。

JetBrains 系列(IntelliJ IDEA / PyCharm / GoLand / WebStorm):按语言分家的 IDE,提供语言级别的深度分析——重构、代码流分析、跨文件依赖理解都比通用编辑器强。适合大型项目开发,对语言特性有更强的感知能力。

Zed:性能导向,GPU 渲染,启动极快。内置实时协作编辑和 AI 集成。生态还在成长,但值得关注。适合追求极致速度或有实时协作需求的场景。

这三个选项都是长期有效的选择,不会在几年内消失。选择的主要依据是团队约定、语言深度需求和个人工作流偏好,而不是哪个更”专业”。

维护原则

先稳定基本功,再加插件hjkl 移动、文本对象操作、撤销重做、搜索替换进入肌肉记忆,再考虑插件化。配置太早反而增加复杂度。

把 Vim 当远程环境底座:本地可以用更完整的 IDE,SSH 进服务器、容器和最小系统时,Vim/Neovim 是最稳的编辑入口。保持这个能力,不被 GUI 环境锁死。

Neovim 配置即代码init.lualua/options.lualua/plugins/ 进入 dotfiles,能在新机器一键恢复。不要把关键配置只存在本机状态里。

不要和 IDE 对抗:Neovim 的价值是键盘驱动、文本环境和可迁移配置。大型调试、可视化重构或团队统一 IDE 工作流,仍然可以交给更合适的工具。