简介:本资源是一份面向嵌入式开发与Linux内核学习者的Source Insight 3.0实战入门教程,专为不熟悉Windows平台下大型C/C++项目源码阅读的开发者设计,解决在无调试环境时快速理解复杂代码结构、高效定位函数调用与变量定义的核心痛点。文档以Word格式(.doc)单文件交付,大小495KB,内容完整覆盖安装配置、项目创建、Add Tree批量导入源码、Reference全局引用追踪、工程窗口与标记浏览等关键操作,并结合Linux 2.4内核四千余个文件的实际导入案例展开说明,附带界面图示与实用技巧(如跳转定义、函数调用图查看、数据库加速配置)。已有509人下载学习,适合初接触Source Insight的中级开发者快速上手,掌握源码级分析能力,显著提升阅读Linux内核等大型开源项目的效率与准确性。
1. Source Insight 不是 IDE,是源码阅读的“显微镜”:专治 Linux 内核看不懂、函数跳转像迷宫、全局搜索等三分钟的工程师
你有没有过这种体验:打开 Linux 2.4 内核四千多个 C 文件,用 Notepad++ 或 VS Code 硬搜do_fork,结果跳进fork.c才发现它只是个壳,真正逻辑在kernel/fork.c里;再 Ctrl+Click 跳转copy_process,却弹出 “symbol not found” —— 不是代码没写,是你手里的编辑器根本没建好符号索引。这不是你水平问题,是工具选错了。Source Insight(SI)从诞生第一天起就不是为写代码服务的,它是为「读透别人写的、超大规模、跨平台、无构建系统的源码」而生的专用阅读引擎。它不编译、不链接、不调试,但能把__do_sys_open→do_sys_open→path_openat→link_path_walk这条调用链,用一张点击即跳的图谱摊在你面前;它能在 3 秒内完成整个内核工程的grep -r "mm_struct"全局引用扫描,并把每处调用都标上红色箭头,点一下就飞过去。它适合谁?不是刚学printf的新手,而是正在啃 eBPF 源码、分析 RTOS 启动流程、逆向驱动模块、或带团队做 Linux BSP 移植的固件/内核/系统工程师——你不需要它帮你生成 Makefile,但你需要它让你在三天内看懂mm/memory.c里页表映射的七层嵌套宏展开。别被“Windows 平台”劝退:今天用 WSL2 + SI 双开,一边make menuconfig一边实时跳转配置项对应的 Kconfig 和 C 实现,才是真实产线节奏。
2. 项目创建与文件加载:为什么Add Tree是唯一正确姿势,以及.s汇编文件为何一片黑白
Source Insight 的核心能力全部建立在「符号数据库」之上,而数据库质量,90% 取决于你第一步怎么建项目。很多人卡在“新建项目→点 OK→打开文件→全是黑字”,本质是数据库没建、类型没认、文件没索引。下面拆解真实工作流,每一步都对应一个可验证的技术动作。
2.1 创建项目时必须勾选的两个关键选项:本地数据库与解析深度
启动 SI 后,Project → New Project,输入项目名(如linux-5.10),点击 OK 后弹出关键设置对话框:
注意:这个对话框里只有两个选项真正影响后续所有功能,其余可默认
- ✅
Use local database (faster searching):必须勾选。SI 会为该项目在%USERPROFILE%\Documents\Source Insight\Projects\<project_name>\下生成.si_*索引文件(如.si_symbols,.si_files)。不勾选=每次搜索都实时遍历所有文件=搜索 5 秒起步。- ✅
Parse files when added to project:必须勾选。这是让 SI 主动解析 C/C++/ASM 文件并提取函数、宏、结构体定义的前提。不勾选=文件加进来了,但Jump to Definition永远报错。
# 验证数据库是否生效:项目创建后,进入项目目录 cd "%USERPROFILE%\Documents\Source Insight\Projects\linux-5.10" dir /b *.si_* # 正常应看到 .si_symbols、.si_files、.si_project 等文件,大小从几 MB 到上百 MB(取决于源码量)参数说明:
.si_symbols是符号表核心,记录每个函数/变量的定义位置(文件+行号);.si_files记录文件依赖关系;.si_project存储项目配置。删除它们等于重置整个索引,下次打开会自动重建。
2.2Add Tree加载全量源码:为什么不用Add All,以及如何避免“文件太多加载失败”
Linux 内核源码动辄上万文件,SI 对单次加载有隐式限制(实测超过 8000 个文件可能触发 UI 卡死)。Add All会尝试一次性将当前目录所有文件(含.git、Documentation/等非代码目录)全塞进项目,极易失败。而Add Tree是递归加载的智能模式,支持路径过滤:
点击
Project → Add and Remove Project Files...在弹出窗口中,点击右下角
Add Tree...在
Directory输入框填入你的内核源码根目录(如D:\src\linux-5.10)关键操作:在
File filter输入框中,精确填写你要索引的文件类型:*.c;*.h;*.S;*.s;*.lds;*.dts;*.dtsi说明:
*.S(大写)是 GCC 编译器预处理后的汇编,*.s(小写)是手写汇编。Linux 内核中arch/x86/kernel/head_64.S是大写,init/main.c是 C,drivers/usb/core/*.c是驱动。漏掉*.s就会导致start_kernel汇编入口找不到定义。勾选
Recurse into subdirectories,点击 OK
避坑提示:如果源码目录下有
build/、output/等编译产物目录,务必先在 Windows 资源管理器中将其属性设为“隐藏”。SI 的Add Tree默认跳过隐藏目录,否则会把build/.tmp_vmlinux.o这类二进制文件也当文本加载,导致索引崩溃。
2.3 让.s汇编文件彩色高亮:Document Type 绑定与文件过滤器重载
加载完*.s文件后,你会发现它们在编辑器里是纯黑白的,无法跳转、无语法高亮——这是因为 SI 默认未将.s关联到汇编解析器。必须手动绑定 Document Type:
Options → Document Options...- 左上角
Document Type下拉菜单,选择x86 Asm Source File(ARM 架构选ARM Asm Source File) - 右侧
File filter框中,在现有内容(如*.asm;*.inc)末尾添加;*.s,变成:*.asm;*.inc;*.s - 点击
OK
血泪经验:这步做完不会立即生效!SI 的 Document Type 绑定是“静态快照”,已加载的
.s文件仍保持旧状态。必须执行:
Project → Synchronize Files...→ 勾选Rescan all files in project→ OK- 或更彻底:
Project → Remove Project Files...先删掉所有.s文件,再重新Add Tree(确保 filter 包含*.s)
验证是否成功:打开任意.s文件(如arch/x86/kernel/head_64.S),应看到movq,call,pushq等指令高亮为蓝色,寄存器%rax为绿色,注释为灰色,且光标放在call start_kernel上按Ctrl+=能跳转到 C 文件定义。
3. 符号跳转与引用分析:Jump to Definition失效的五个真相,以及Reference结果集管理的玄学
SI 最被神化的功能是“一键跳转”,但现实中Ctrl+=报 “symbol not found” 是高频翻车现场。这不是软件 Bug,而是符号解析链路上某个环节断了。下面列出真实产线中 95% 的跳转失败原因及闭环解决方案。
3.1Jump to Definition失效的五大根因与逐级排查法
| 现象 | 原因 | 解决方案 | 验证命令 |
|---|---|---|---|
光标在printk()上按Ctrl+=,弹窗报错 | printk是宏(#define printk(fmt, ...) ...),SI 默认不展开宏定义 | Options → Preference → Symbol Lookups→ 勾选Expand macros when searching for definitions | 重启 SI 后重试 |
跳转到struct task_struct定义,却停在include/linux/sched.h第一行#ifndef _LINUX_SCHED_H | 头文件保护宏阻断了解析,SI 未识别#include依赖链 | Options → Document Options → C/C++ Source File→Parsing选项卡 → 勾选Parse #include files | 重新Synchronize Files |
open()系统调用跳转到fs/open.c,但sys_open函数内do_sys_open又跳不到 | do_sys_open定义在fs/exec.c,但该文件未加入项目 | Project → Add and Remove Project Files...→ 手动添加fs/exec.c | Project → Project Symbols查看do_sys_open是否在列表中 |
在drivers/net/ethernet/intel/igb/igb_main.c中跳转pci_read_config_word,失败 | pci_read_config_word定义在drivers/pci/access.c,但drivers/pci/目录未加入项目 | Add Tree时需包含drivers/pci/路径,不能只加drivers/net/ | Search → Search Project搜pci_read_config_word,确认是否被索引 |
跳转__attribute__((section(".init.text")))标记的函数失败 | SI 无法解析 GCC 特殊属性,认为该函数是“未定义符号” | 手动在Options → Document Options → C/C++ Source File→Parsing选项卡 →Add preprocessor symbol输入__attribute__ | 重启 SI,重新同步 |
关键技巧:当不确定哪个环节断了,用
Search → Search Project(快捷键Ctrl+Shift+F)直接搜函数名。如果搜不到,说明根本没进索引;如果搜到了但跳转失败,说明是宏/属性/头文件依赖问题。
3.2Reference全局引用:如何避免结果集爆炸,以及红色箭头的正确打开方式
References(快捷键Ctrl+Shift+R)是 SI 的核武器——它能瞬间列出kmalloc在整个内核中被调用的所有位置。但新手常陷入两个误区:一是结果太多刷屏卡死,二是点了红色箭头却跳到错误行。
▶ 结果集管理:取代 vs 追加的决策逻辑
- 首次搜索
kmalloc:得到 237 个引用,SI 自动显示为“集中模式”(Concentrated View),所有结果堆在一个窗口,左侧有红色箭头按钮。 - 第二次搜索
kfree:SI 弹窗问 “Append to current list or replace?”- ✅选
Replace:如果你只想专注kfree的调用点,这是最干净的选择。 - ⚠️选
Append:仅当你需要对比kmalloc/kfree配对情况时才用,但注意:追加后无法按“来源文件”分组筛选,所有 400+ 行混在一起,Ctrl+Up/Down只能顺序滚动,无法跳转到特定文件。
- ✅选
生产建议:永远选
Replace。需要多关键词交叉分析时,用Search → Search Project的高级模式,输入kmalloc\|kfree(正则 OR),结果天然分组。
▶ 红色箭头的两种模式切换
- 集中模式(Concentrated View):结果以
<文件名>:<行号>列表形式显示,每行前有红色箭头。点击箭头 = 切换到“详细模式”(Detailed View),即在编辑器中精准定位到该行,并高亮显示。 - 详细模式(Detailed View):编辑器光标停在目标行,左侧栏出现红色箭头图标。此时再点该箭头 = 切回集中模式,方便快速比对上下文。
- 快捷导航:在详细模式下,用
Alt+Up/Alt+Down可在本次Reference的所有结果间快速跳转,无需鼠标。
// 示例:在 fs/read_write.c 中某行 ret = kmalloc(size, GFP_KERNEL); // 光标在此行,按 Ctrl+Shift+R // SI 弹出 Reference 窗口,第一行可能是: // drivers/scsi/sg.c:1234: buf = kmalloc(len, GFP_KERNEL); // 点击该行前红色箭头 → 自动打开 drivers/scsi/sg.c,光标跳到 1234 行避坑 / 常见问题 / 排查
现象:
Reference搜索后,部分结果行没有红色箭头,或点击无反应
原因:该行所在文件未被 SI 加载(如drivers/xxx/yyy.c不在项目中),或该行是注释/空行/预处理指令
解决:检查Project → Project Files是否包含该文件;用Search → Search Project确认该符号是否被索引现象:
Reference结果中显示include/linux/mm.h:45,但打开该头文件后第 45 行是#endif
原因:SI 的行号计算包含所有#include展开后的虚拟行,实际物理行号偏移。include/linux/mm.h第 45 行在预处理后可能是struct page { ... }定义
解决:不要纠结物理行号,以Jump to Definition跳转到符号定义为准现象:在
arch/arm64/kernel/head.S中搜索el2_setup,Reference显示 3 处调用,但其中一处跳转后是bl el2_setup指令,另一处却是adrp x0, el2_setup
原因:SI 将所有含el2_setup字符串的位置都算作“引用”,包括地址加载指令。这不是错误,是设计使然
解决:人工过滤,bl是调用,adrp是取地址,语义不同现象:
Reference搜索CONFIG_DEBUG_PAGEALLOC,结果全是#ifdef CONFIG_DEBUG_PAGEALLOC,无法跳转到 Kconfig 定义
原因:Kconfig 文件(init/Kconfig)未被 SI 当作 C 源码解析,其语法不被支持
解决:Project → Add and Remove Project Files...添加init/Kconfig,然后Options → Document Options将其 Document Type 设为Kconfig File(需提前安装 Kconfig 插件或手动配置)现象:
Reference搜索__init,结果爆炸(上千行),全是static int __init xxx_init(void)
原因:__init是宏(#define __init __section(.init.text)),SI 将其作为字符串匹配
解决:用正则搜索static\s+int\s+\w+\s*\(\s*void\s*\)\s*__init,或直接搜函数名(如xxx_init)
4. SourceLink 日志解析:把make编译错误秒变可点击的源码跳转,告别手动复制粘贴
当make -j8报错ERROR: "some_symbol" [drivers/xxx/yyy.ko] undefined!,传统做法是复制drivers/xxx/yyy.c:123,再手动在 SI 里File → Open,再Ctrl+G跳转到 123 行。SourceLink 功能能让这个过程变成一次点击——它把编译器输出的每一行错误,自动转换成可点击的源码链接。这才是 SI 真正融入开发流的核心能力。
4.1 SourceLink 原理:正则表达式捕获组驱动的智能解析
SourceLink 的本质是「正则匹配 + 分组提取 + 跳转」。SI 支持两种模式:
File, then line:匹配形如Error d:\src\linux\fs\open.c 123: ...的格式,提取d:\src\linux\fs\open.c(文件)和123(行号)Line, then file:匹配形如fs/open.c:123: error: ...的格式,提取fs/open.c和123
关键在于正则表达式中的括号()必须严格对应两个捕获组:第一个(...)是文件路径,第二个(...)是行号。SI 会把第一个组的内容当作文件名,第二个组当作行号,执行跳转。
4.2 配置 GCC 编译器输出的 SourceLink:适配make错误日志
Linux 内核编译时,GCC 默认输出格式为fs/open.c:123: error: ...,属于Line, then file模式。配置步骤:
Search → Parse Source Links...Mode选择Line, then filePattern输入正则表达式:([^:]+):([0-9]+):解释:
[^:]+匹配除冒号外的任意字符(即文件名fs/open.c),:匹配字面冒号,([0-9]+)匹配数字行号,最后的:匹配错误信息前的冒号。两个括号即两个捕获组。- 点击
OK
验证:打开一个包含编译错误的 log 文件(如
make.log),光标放在fs/open.c:123: error: ...行,按Ctrl+Shift+R(Reference),该行左侧会出现红色箭头,点击即跳转到fs/open.c第 123 行。
4.3 集成到自定义命令:让make命令输出自动带 SourceLink
手动解析日志太慢。SI 支持将make命令设为自定义命令,运行后自动捕获输出并创建 SourceLink:
Options → Custom Commands...- 点击
Add,Command name填Make Kernel Run框输入:make -C D:\src\linux-5.10 M=$(CurDir) modules说明:
$(CurDir)是 SI 内置变量,代表当前文件所在目录。这样你在drivers/net/ethernet/intel/igb/下右键运行,就会自动make -C linux-5.10 M=drivers/net/ethernet/intel/igbDir框留空(自动使用当前文件目录)- 勾选
Capture Output和Parse Source Links in Output Source Links Mode选择Line, then file,Pattern填([^:]+):([0-9]+):- 点击
OK
使用:在任意
.c文件中,右键 →Custom Commands → Make Kernel,编译输出直接显示在Output窗口,所有xxx.c:123:错误行自带红色箭头,点击即跳。
# 输出示例(Output 窗口): CC [M] drivers/net/ethernet/intel/igb/igb_main.o drivers/net/ethernet/intel/igb/igb_main.c:1234: error: implicit declaration of function 'some_undefined_func' # 点击该行前红色箭头 → 自动打开 igb_main.c,光标定位到 1234 行避坑 / 常见问题 / 排查
现象:
Custom Command运行make后,Output窗口无红色箭头
原因:Parse Source Links in Output未勾选,或Source Links Mode与实际输出格式不匹配(如用了File, then line模式去解析xxx.c:123:)
解决:检查Custom Commands设置,用Search → Parse Source Links...手动测试正则是否匹配现象:
Output窗口显示drivers/net/ethernet/intel/igb/igb_main.c:1234:,但点击箭头后 SI 提示 “File not found”
原因:该文件路径是相对路径,而 SI 的 SourceLink 默认在项目根目录下查找。drivers/net/...应相对于D:\src\linux-5.10\
解决:修改Pattern为绝对路径匹配,或Options → Preference → Files→Base directory for relative paths设为D:\src\linux-5.10现象:
make输出中有中文(如错误:),导致正则匹配失败
原因:GCC 默认输出英文,若系统 locale 为中文,需强制LANG=C make
解决:Run框改为LANG=C make -C D:\src\linux-5.10 M=$(CurDir) modules现象:
Output窗口内容过多,SourceLink 只对前 100 行生效
原因:SI 对捕获输出有默认长度限制(约 64KB)
解决:Options → Preference → Files→Maximum output capture size (KB)改为512现象:
make编译通过,但Output窗口显示warning: ...,这些 warning 行没有 SourceLink
原因:默认Pattern只匹配error:,未覆盖warning:
解决:将Pattern改为([^:]+):([0-9]+):\s*(error|warning):,即可同时捕获 error 和 warning
5. 宏与自动化:用InsFunHeader自动生成函数头,以及Smart Rename重命名的边界条件
Source Insight 的宏系统(.em文件)是它区别于其他编辑器的灵魂——不是简单的文本替换,而是基于上下文的智能代码生成。配合Smart Rename,能完成从“添加函数注释”到“重构函数名”的完整闭环。但这两个功能都有严格的触发条件,踩坑成本极高。
5.1 宏文件加载与InsFunHeader实战:三步生成符合 Linux Coding Style 的函数头
SI 的宏必须加载到Base工程才能全局调用。网上流传的Gaoke.em或t357.em宏文件,若未正确加载,InsFunHeader按下后只会弹窗报错。
▶ 加载宏文件的强制三步
Project → Open Project...→ 导航到%USERPROFILE%\Documents\Source Insight\Projects\Base\,打开Base.prj注意:
Base.prj是 SI 的内置宏工程,必须存在。若被误删,从 SI 安装目录Program Files\Source Insight 4.0\Projects\Base.prj复制一份Project → Add and Remove Project Files...→ 点击Add,选择你的.em文件(如t357.em)→OKOptions → Menu Assignments...→ 在Command框输入macro→ 在下方列表找到InsFunHeader→ 点击Assign...→ 按Ctrl+Shift+H(或其他你喜欢的组合键)→OK
▶InsFunHeader使用流程与 Linux 风格适配
- 在
.c文件中,将光标放在函数名上(如static int my_driver_probe(struct platform_device *pdev)的my_driver_probe上) - 按
Ctrl+Shift+H - 弹窗输入
Information of function(如Probe platform device and init hardware) - 弹窗输入
Description of function(如Initialize the driver's private data structure, request IRQ, and map I/O memory.) - 回车确认
// 生成效果(完全符合 Linux kernel-doc 格式) /** * my_driver_probe - Probe platform device and init hardware * @pdev: pointer to platform device * * Initialize the driver's private data structure, request IRQ, and map I/O memory. * * Return: 0 on success, negative errno on failure. */ static int my_driver_probe(struct platform_device *pdev) {参数说明:宏中
GetCurSymbol()获取光标下函数名,GetSymbolLine()获取定义行号,Ask()弹窗获取用户输入。生成的注释块自动插入在函数定义上方,且@pdev参数名从函数签名中提取,Return行根据返回类型(int)智能生成。
5.2Smart Rename:为什么Ctrl+'有时失效,以及数组名重命名的唯一解法
Smart Rename(Ctrl+')是 SI 最强大的重构工具,但它不是简单字符串替换,而是基于符号作用域的智能重命名。其成败取决于三个前提:光标位置、上下文匹配、文档类型。
▶ 成功重命名的黄金条件
- ✅光标必须精确落在要重命名的符号上(如
int old_var;的old_var,不能在int或;上) - ✅该符号必须已被 SI 索引(
Project → Project Symbols中能搜到) - ✅Document Type 必须正确(
.c文件需为C/C++ Source File,不能是Text File)
▶ 数组名重命名的玄学解法
问题:int buffer[1024];,光标放在buffer上按Ctrl+',输入new_buffer,报错 “Cannot rename array name”。
原因:SI 将buffer[1024]视为“数组定义”,而buffer本身是标识符,但Smart Rename默认不处理数组声明中的标识符。
唯一解法:
- 将光标移到
buffer后的[上(即buffer[) - 按
Ctrl+'→Old Name自动填充buffer[ - 在
New Name中输入new_buffer[(保留[) - 勾选
Skip Comments,取消Confirm Each Replacement - 点击
Rename
原理:SI 将
buffer[识别为“数组访问”上下文,此时buffer被当作可重命名的符号处理。生成的new_buffer[1024]完美保留数组维度。
▶Smart Reference Matching开关的实战取舍
- ✅勾选
Smart Reference Matching:重命名只在相同作用域生效。如struct foo { int bar; };中的bar,重命名为baz,不会误改全局变量int bar;。推荐日常开启。 - ⚠️取消勾选:全局暴力替换,
bar出现在任何地方都改。仅用于清理废弃变量名,且必须先Search Project确认无误。
避坑 / 常见问题 / 排查
现象:
Smart Rename后,Search Results窗口显示 0 个替换
原因:Old Name框中显示的是完全限定名(如driver_probe),但你输入了probe(不匹配)
解决:光标必须放在符号上,让 SI 自动填充Old Name,不要手动输入现象:重命名
list_add为my_list_add,但list_add_tail也被改成了my_list_add_tail
原因:Smart Rename默认启用子字符串匹配(list_add是list_add_tail的子串)
解决:Options → Preference → Symbol Lookups→ 取消Match substrings in symbol names现象:重命名
struct device中的name成员,Search Results显示修改了struct platform_device.name,但struct usb_device.name未改
原因:platform_device继承自device,SI 识别了继承关系;usb_device是独立结构体,未被关联
解决:手动添加usb_device到项目,或用Search → Search Project全局替换(关闭Smart Reference Matching)现象:
Smart Rename后,.h头文件中的extern int old_var;未被更新
原因:extern声明未被 SI 当作“定义”,只索引了.c中的定义
解决:Options → Document Options → C/C++ Source File→Parsing选项卡 → 勾选Parse extern declarations现象:重命名
__init宏,结果所有__init字符串都被替换成__new_init,包括#define __init ...
原因:__init是宏,Smart Rename无法区分宏定义与宏使用
解决:禁用Smart Rename,用Search → Replace in Files,勾选Match whole word only,仅替换__init作为独立单词的位置
6. 生产环境终极调优:字体、缩进、小键盘修复,以及从那以后我每次新建项目都强制走一遍的 checklist
SI 的默认配置是为通用场景设计的,但在 Linux 内核阅读这种高强度、长周期、多文件并行的场景下,几个看似微小的 UI/UX 细节,会直接决定你每天是高效还是烦躁。下面这些配置,是我带三个内核团队五年沉淀下来的“血泪 checklist”,每一条都对应一个真实翻车现场。
6.1 字体与等宽对齐:为什么Courier New是唯一选择,以及如何禁用 VERDANA 的“美观陷阱”
SI 默认字体Verdana是比例字体(proportional font),i和W宽度不同。这在网页阅读很舒服,但在代码里是灾难:
// VERDANA 下(错误对齐) int i = 0; // i 占 1 个像素宽度 int width = 100; // width 占 5 个像素宽度 // 结果:等号列无法对齐,结构体成员偏移肉眼难辨 // Courier New 下(正确对齐) int i = 0; // 每个字符固定宽度 int width = 100;强制设置等宽字体:Options → Preference → Fonts→Editor font→ 点击Change...→ 字体选Courier New,字号10(100% DPI 下清晰),勾选Bold(增强可读性)→OK
验证:打开任意
.c文件,选中一段含=的代码,按Tab缩进,观察所有=是否严格垂直对齐。不对齐则字体未生效。
6.2 Tab 与缩进:彻底禁用智能缩进,回归 Linux Kernel Coding Style 的 8 空格
Linux 内核强制使用Tab=8 个空格,且if/for后不自动缩进{。SI 默认的Auto Indent会破坏这一规则。
四步禁用所有智能缩进:
Options → Preference → Typing→ 取消勾选:Typing tab indents line, regardless of selectionTyping tab replaces current selectionUse automatic symbol completion window(自动补全干扰阅读)
Options → Document Options...→Document Type选C/C++ Source File→Editing Options:Tab width=8Indent width=8- ✅
Expand tabs(按 Tab 键 = 插入 8 个空格) - ❌
Auto Indent(彻底关闭智能缩进)
Options → Document Options...→C/C++ Source File→Auto Indent→Smart→ 取消Indent Open Brace和Indent Close BraceOptions → Key Assignments...→ 搜索tab→ 将Insert Tab的快捷
本文还有配套的精品资源,点击获取