编辑器不是身份认同,是工具。这篇文章覆盖两条路:终端编辑器(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(插入模式):按
i、a、o进入,这时输入的字符才会出现在文件里。按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-fugitive | Git 操作集成 | :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 次
@@ " 重复上一个宏典型用例:给多行加序号、批量修改相似结构、整理临时数据文件。录制时注意用可重复的移动(j、w),而不是绝对行号。
块可视模式多行编辑
Ctrl+v 进入块可视模式,选中一列,然后:
I+ 输入 +Esc:在每行选中列之前插入相同文本d/x:删除选中列r{c}:替换选中列的所有字符为 c
常用于给多行注释掉代码、对齐赋值语句等。
6. Neovim:现代化的那条路
什么时候选 Neovim
Neovim 是 Vim 的现代分支,保留了完整的模态编辑能力,同时重构了底层架构:
| 特性 | Vim | Neovim |
|---|---|---|
| 配置语言 | Vimscript | Lua(一等公民)+ 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.lualua/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 = truelua/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.lua、lua/options.lua、lua/plugins/ 进入 dotfiles,能在新机器一键恢复。不要把关键配置只存在本机状态里。
不要和 IDE 对抗:Neovim 的价值是键盘驱动、文本环境和可迁移配置。大型调试、可视化重构或团队统一 IDE 工作流,仍然可以交给更合适的工具。