<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://bill.is-a.dev/</id>
  <title type="text">一只比尔</title>
  <subtitle type="text">Call Me Bill</subtitle>
  <updated>2026-09-03T00:00:00.000Z</updated>
  <author><name>CMBill</name></author>
  <link rel="alternate" href="https://bill.is-a.dev/"/>
  <link rel="self" href="https://bill.is-a.dev/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.8</generator>
    <entry>
      <id>https://bill.is-a.dev/posts/ubuntu-install-cmake/</id>
      <title type="text">在 Ubuntu 上安装更高版本的 CMake</title>
      <published>2026-09-03T00:00:00.000Z</published>
      <updated>2026-09-03T00:00:00.000Z</updated>
      <author><name>CMBill</name></author>
      <link rel="alternate" href="https://bill.is-a.dev/posts/ubuntu-install-cmake/"/>
      <summary type="text">在较老的 Ubuntu 版本上通过添加 Kitware 官方 APT 仓库安装更高版本 CMake 的完整指南，涵盖安装依赖、获取 GPG 签名密钥、按发行版添加仓库、安装 keyring 及 CMake 等步骤。</summary>
      <content type="html"><![CDATA[<p>参考：<a href="https://apt.kitware.com/" target="_blank">Kitware APT Repository</a></p>
<p>对于较老的 Ubuntu 版本，其包管理器中提供的 CMake 往往也是很老的版本，无法编译某些要求更高的程序。可以通过添加 CMake 官方提供的 Kitware APT Repository 来安装更高版本的 CMake。</p>
<section><h2>1 安装依赖<a href="#1-安装依赖"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>update</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>install</span><span> </span><span>ca-certificates</span><span> </span><span>gpg</span><span> </span><span>wget</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>2 获取签名密钥<a href="#2-获取签名密钥"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>test</span><span> </span><span>-f</span><span> </span><span>/usr/share/doc/kitware-archive-keyring/copyright</span><span> </span><span>||</span></div></div><div><div><div>2</div></div><div><span>wget</span><span> </span><span>-O</span><span> </span><span>-</span><span> </span><span>https://apt.kitware.com/keys/kitware-archive-latest.asc</span><span> </span><span>2&gt;</span><span>/dev/null</span><span> </span><span>|</span><span> </span><span>gpg</span><span> </span><span>--dearmor</span><span> </span><span>-</span><span> </span><span>|</span><span> </span><span>sudo</span><span> </span><span>tee</span><span> </span><span>/usr/share/keyrings/kitware-archive-keyring.gpg</span><span> </span><span>&gt;</span><span>/dev/null</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>3 添加仓库<a href="#3-添加仓库"><span>#</span></a></h2><p>Ubuntu Resolute Raccoon (26.04):</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>echo</span><span> </span><span>'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ resolute-rc main'</span><span> </span><span>|</span><span> </span><span>sudo</span><span> </span><span>tee</span><span> </span><span>-a</span><span> </span><span>/etc/apt/sources.list.d/kitware.list</span><span> </span><span>&gt;</span><span>/dev/null</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>update</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>Ubuntu Noble Numbat (24.04):</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>echo</span><span> </span><span>'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ noble main'</span><span> </span><span>|</span><span> </span><span>sudo</span><span> </span><span>tee</span><span> </span><span>/etc/apt/sources.list.d/kitware.list</span><span> </span><span>&gt;</span><span>/dev/null</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>update</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>Ubuntu Jammy Jellyfish (22.04):</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>echo</span><span> </span><span>'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ jammy main'</span><span> </span><span>|</span><span> </span><span>sudo</span><span> </span><span>tee</span><span> </span><span>/etc/apt/sources.list.d/kitware.list</span><span> </span><span>&gt;</span><span>/dev/null</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>update</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>Ubuntu Focal Fossa (20.04):</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>echo</span><span> </span><span>'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ focal main'</span><span> </span><span>|</span><span> </span><span>sudo</span><span> </span><span>tee</span><span> </span><span>/etc/apt/sources.list.d/kitware.list</span><span> </span><span>&gt;</span><span>/dev/null</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>update</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>4 安装 keyring<a href="#4-安装-keyring"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>install</span><span> </span><span>kitware-archive-keyring</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>5 安装 CMake<a href="#5-安装-cmake"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>install</span><span> </span><span>cmake</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>]]></content>
    </entry>
    <entry>
      <id>https://bill.is-a.dev/posts/gcm-on-ubuntu/</id>
      <title type="text">在 Ubuntu 上使用 Git Credential Manager（GCM）实现认证</title>
      <published>2026-08-05T00:00:00.000Z</published>
      <updated>2026-08-05T00:00:00.000Z</updated>
      <author><name>CMBill</name></author>
      <link rel="alternate" href="https://bill.is-a.dev/posts/gcm-on-ubuntu/"/>
      <summary type="text">在 Ubuntu 上从 GitHub Release 下载安装 Git Credential Manager（GCM），配置其使用 Seahorse（GNOME Keyring）作为凭证存储后端，实现访问私有仓库时通过浏览器完成登录认证。</summary>
      <content type="html"><![CDATA[<p>Git Credential Manager（GCM）是一个跨平台的凭证助手，能够安全地存储 Git 凭证并自动完成身份验证，同时支持 OAuth、双因素认证（2FA）等现代认证方式，且支持多账户场景，会根据不同的托管平台和仓库地址自动匹配对应的凭证。本文将介绍如何在 Ubuntu 系统上从 GitHub Release 下载安装 GCM，并配置其使用 Ubuntu 自带的 Seahorse（GNOME Keyring）作为凭证存储后端，最终实现访问私有仓库时通过浏览器完成登录认证的完整流程。</p>
<section><h2>1 从 GitHub Release 下载安装 GCM<a href="#1-从-github-release-下载安装-gcm"><span>#</span></a></h2><p>GCM 官方维护在 GitHub 上，项目地址为 <a href="https://github.com/git-ecosystem/git-credential-manager" target="_blank">https://github.com/git-ecosystem/git-credential-manager</a>。Ubuntu/Debian 系统可以通过 <code>.deb</code> 包进行安装。</p><section><h3>1.1 下载最新的 <code>.deb</code> 包<a href="#11-下载最新的-deb-包"><span>#</span></a></h3><p>访问 GCM 的 <a href="https://github.com/git-ecosystem/git-credential-manager/releases" target="_blank">Releases 页面</a>，找到最新版本的 <code>.deb</code> 包。通常文件名为 <code>gcm-linux-x64-{version}.deb</code>。</p></section><section><h3>1.2 安装 <code>.deb</code> 包<a href="#12-安装-deb-包"><span>#</span></a></h3><p>记得更换为实际的路径。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>./path/to/gcm-linux-x64-{version}.deb</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>2 配置 GCM 使用 Seahorse 作为凭证存储<a href="#2-配置-gcm-使用-seahorse-作为凭证存储"><span>#</span></a></h2><p>与 Windows 和 macOS 不同，Linux 系统没有默认的全局凭证存储机制，因此需要手动配置 GCM 使用哪种凭证存储后端。Ubuntu 系统自带 GNOME Keyring，可以通过 Seahorse（“密码与密钥”应用）进行图形化管理。</p><section><h3>2.1 配置 credential.helper<a href="#21-配置-credentialhelper"><span>#</span></a></h3><p>将 GCM 设置为 Git 的凭证助手：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>config</span><span> </span><span>--global</span><span> </span><span>credential.helper</span><span> </span><span>manager</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.2 配置 credential.credentialStore 为 secretservice<a href="#22-配置-credentialcredentialstore-为-secretservice"><span>#</span></a></h3><p>GCM 在 Linux 上支持多种凭证存储方式，包括 <code>secretservice</code>（基于 freedesktop.org Secret Service API）、<code>gpg</code>（基于 GPG 加密）以及 <code>cache</code>（内存缓存）等。其中 <code>secretservice</code> 使用 <code>libsecret</code> 库通过 D-Bus 与 GNOME Keyring 或 KDE Wallet 等桌面凭证管理器交互，在 Ubuntu 上由 Seahorse（GNOME Keyring）提供支持。</p><p>执行以下命令将凭证存储设置为 <code>secretservice</code>：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>config</span><span> </span><span>--global</span><span> </span><span>credential.credentialStore</span><span> </span><span>secretservice</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><p>也可以通过环境变量方式配置：<code>export GCM_CREDENTIAL_STORE=secretservice</code>。</p></blockquote></section><section><h3>2.3 确保依赖已安装<a href="#23-确保依赖已安装"><span>#</span></a></h3><p><code>secretservice</code> 存储方式需要 <code>libsecret</code> 库的支持。在 Ubuntu 上通常已经预装，如果没有，可以手动安装：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>update</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>libsecret-1-0</span><span> </span><span>libsecret-1-dev</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.4 重启系统<a href="#24-重启系统"><span>#</span></a></h3><p>配置完成后，建议重启系统以确保所有更改生效。如果不重启，可能会遇到一些奇怪的错误。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>reboot</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>3 访问私有仓库——浏览器登录认证<a href="#3-访问私有仓库浏览器登录认证"><span>#</span></a></h2><p>完成安装和配置后，GCM 会在 Git 需要认证时自动介入。</p><section><h3>3.1 首次认证流程<a href="#31-首次认证流程"><span>#</span></a></h3><p>当你首次执行需要认证的 Git 操作（如 <code>git clone</code> 访问私有仓库、<code>git push</code> 等）时，GCM 会自动启动系统的默认浏览器，打开托管平台（如 GitHub）的授权页面。</p><p>例如，执行：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>clone</span><span> </span><span>https://github.com/your-username/private-repo.git</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>GCM 会弹出一个窗口，提示你使用浏览器登录。在浏览器中完成登录和授权后，凭证会被安全地存储在 GNOME Keyring 中，后续操作将自动使用已存储的凭证，无需再次输入。</p></section><section><h3>3.2 查看已存储的凭证<a href="#32-查看已存储的凭证"><span>#</span></a></h3><p>Ubuntu 用户可以通过 Seahorse（“密码与密钥”应用）查看 GCM 存储的凭证：</p><ol>
<li>打开 Seahorse 应用（在应用菜单中搜索“密码与密钥”或 <code>seahorse</code>）</li>
<li>在左侧面板中找到“Login”密钥环</li>
<li>查找类似 <code>git:https://github.com/</code> 的条目</li>
</ol></section><section><h3>3.3 更新或删除凭证<a href="#33-更新或删除凭证"><span>#</span></a></h3><p>如果需要更新凭证（例如更换了 GitHub 密码或 Personal Access Token），可以通过 Seahorse 删除旧凭证，然后在下一次 Git 操作时重新通过浏览器认证即可：</p><ol>
<li>打开 Seahorse</li>
<li>在“登录”密钥环中找到对应的 Git 凭证条目</li>
<li>右键删除</li>
<li>下次执行 <code>git push</code> 或 <code>git clone</code> 时，GCM 会再次打开浏览器进行认证</li>
</ol></section></section>]]></content>
    </entry>
    <entry>
      <id>https://bill.is-a.dev/posts/ubuntu-install-nvidia/</id>
      <title type="text">Ubuntu 安装 nVidia 相关</title>
      <published>2026-07-13T00:00:00.000Z</published>
      <updated>2026-07-13T00:00:00.000Z</updated>
      <author><name>CMBill</name></author>
      <link rel="alternate" href="https://bill.is-a.dev/posts/ubuntu-install-nvidia/"/>
      <summary type="text">Ubuntu 系统下安装 nVidia 显卡驱动、CUDA Toolkit 和 cuDNN 的完整指南，包含禁用 nouveau、添加官方存储库等步骤。</summary>
      <content type="html"><![CDATA[<p>参考：<a href="https://developer.nvidia.com/cuda-downloads" target="_blank">CUDA Toolkit Downloads</a>、<a href="https://blog.mvpbang.com/p/caa2e4a241564356b11e038e3da408fc/" target="_blank">ubuntu下cuda-keyring_1.1-1_all.deb包作用</a></p>
<section><h2>1 安装 CUDA 与显卡驱动<a href="#1-安装-cuda-与显卡驱动"><span>#</span></a></h2><section><h3>1.1 关闭系统自带驱动 nouveau<a href="#11-关闭系统自带驱动-nouveau"><span>#</span></a></h3><p>先通过命令<code>lsmod | grep nouveau</code>查看 nouveau 驱动的启用情况，若无输出则表示已经禁用。</p><p>要禁用 nouveau 驱动，编辑<code>/etc/modprobe.d/blacklist.conf</code>文件，在末尾加上这两行并保存：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>blacklist nouveau</span></div></div><div><div><div>2</div></div><div><span>options nouveau modeset=0</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>随后在终端中：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>update-initramfs</span><span> </span><span>-u</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后重启即可。</p></section><section><h3>1.2 添加 NVIDIA 存储库<a href="#12-添加-nvidia-存储库"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wget</span><span> </span><span>https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-keyring_1.1-1_all.deb</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>dpkg</span><span> </span><span>-i</span><span> </span><span>cuda-keyring_1.1-1_all.deb</span></div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>update</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>2 安装显卡驱动<a href="#2-安装显卡驱动"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>nvidia-open</span><span> </span><span># 会安装最新版本的显卡驱动</span></div></div><div><div><div>2</div></div><div><span># 或者指定版本</span></div></div><div><div><div>3</div></div><div><span># sudo apt-get install -y nvidia-open-580</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><div><div></div><div>Tip</div></div><div><p>Ubuntu 22.04 系统安装时可能会编译报错，是由于不支持系统默认安装的 GCC 11 版本，手动安装 GCC 12 即可。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>gcc-12</span></div></div></code></pre><div><div></div><div></div></div></figure></div></div></div><p>安装完成后，运行<code>nvidia-smi</code>即可检查是否安装成功。</p><p>对于大部分情况，安装好显卡驱动即可，不需执行后续操作。</p></section>
<section><h2>3 安装 CUDA Toolkit<a href="#3-安装-cuda-toolkit"><span>#</span></a></h2><p>安装好显卡驱动后，即已经完成 CUDA 相关运行时的安装。</p><p>最新版本的 CUDA Toolkit 可以参考 <a href="https://developer.nvidia.com/cuda-downloads" target="_blank">CUDA Toolkit Downloads</a>，对于 Ubuntu 20.04 最高支持到 CUDA 12.9，因此参考<a href="https://developer.nvidia.com/cuda-12-9-0-download-archive?target_os=Linux&amp;target_arch=x86_64&amp;Distribution=Ubuntu&amp;target_version=20.04&amp;target_type=deb_network" target="_blank">CUDA Toolkit 12.9 Downloads</a>。在前面已经添加好存储库后直接运行：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>-y</span><span> </span><span>install</span><span> </span><span>cuda-toolkit-13-3</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>随后编辑<code>~/.bashrc</code>配置环境变量。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> CUDA_HOME</span><span>=</span><span>/usr/local/cuda</span></div></div><div><div><div>2</div></div><div><span>export</span><span> PATH</span><span>=</span><span>$PATH:$CUDA_HOME/bin</span></div></div><div><div><div>3</div></div><div><span>export</span><span> LD_LIBRARY_PATH</span><span>=</span><span>$LD_LIBRARY_PATH:$CUDA_HOME/lib64</span></div></div><div><div><div>4</div></div><div><span>export</span><span> LIBRARY_PATH</span><span>=</span><span>$LIBRARY_PATH:$CUDA_HOME/lib64</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>重启终端后运行<code>nvcc -V</code>查看安装信息。</p></section>
<section><h2>4 安装 cuDNN<a href="#4-安装-cudnn"><span>#</span></a></h2><p>最新版本的 cuDNN 可以参考 <a href="https://developer.nvidia.com/cudnn-downloads" target="_blank">cuDNN Downloads</a>，对于 Ubuntu 20.04 最高支持到 cuDNN 9.10.2，因此参考 <a href="https://developer.nvidia.com/cudnn-9-10-2-download-archive?target_os=Linux&amp;target_arch=x86_64&amp;Distribution=Ubuntu&amp;target_version=20.04&amp;target_type=deb_network" target="_blank">cuDNN 9.10.2 Downloads</a>。在前面已经添加好存储库后直接运行：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 对于 CUDA 12，执行</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>-y</span><span> </span><span>install</span><span> </span><span>cudnn-cuda-12</span></div></div><div><div><div>3</div></div><div><span># 对于 CUDA 13，执行</span></div></div><div><div><div>4</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>-y</span><span> </span><span>install</span><span> </span><span>cudnn-cuda-13</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>]]></content>
    </entry>
    <entry>
      <id>https://bill.is-a.dev/posts/opencode-usage-guide/</id>
      <title type="text">OpenCode 以及第三方桌面应用 OpenChamber 的安装与使用</title>
      <published>2026-06-15T00:00:00.000Z</published>
      <updated>2026-06-15T00:00:00.000Z</updated>
      <author><name>CMBill</name></author>
      <link rel="alternate" href="https://bill.is-a.dev/posts/opencode-usage-guide/"/>
      <summary type="text">介绍 OpenCode 的安装与配置方法，包括安装脚本、NPM 安装、供应商添加以及第三方桌面应用 OpenChamber 的使用。</summary>
      <content type="html"><![CDATA[<section><h2>1 安装<a href="#1-安装"><span>#</span></a></h2><div><div><div></div><div>Tip</div></div><div><p>Windows 安装直接跳到 <a href="#122-windows-%E5%AE%89%E8%A3%85-node">Windows 安装 Node</a> 即可。</p></div></div><section><h3>1.1 安装脚本<a href="#11-安装脚本"><span>#</span></a></h3><p>对于 Linux 和 macOS 系统，可以使用安装脚本直接安装。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>curl</span><span> </span><span>-fsSL</span><span> </span><span>https://opencode.ai/install</span><span> </span><span>|</span><span> </span><span>bash</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果网络下载速度过慢或失败，以及 Windows 用户，可以使用 NPM 安装。</p></section><section><h3>1.2 使用 NPM 安装<a href="#12-使用-npm-安装"><span>#</span></a></h3><p>首先确保安装有 Node 环境，参考 <a href="https://nodejs.org/zh-cn/download" target="_blank">下载 Node.js</a>。</p><section><h4>1.2.1 Linux 安装 Node<a href="#121-linux-安装-node"><span>#</span></a></h4><p>对于 Ubuntu，可以使用以下命令安装：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 下载并安装 nvm（此处使用了 gh-proxy 加速）：</span></div></div><div><div><div>2</div></div><div><span>curl</span><span> </span><span>-o-</span><span> </span><span>https://gh-proxy.org/https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh</span><span> </span><span>|</span><span> </span><span>bash</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>\.</span><span> </span><span>"</span><span>$HOME</span><span>/.nvm/nvm.sh"</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span># 使用 nvm 下载并安装最新 LTS 版本的 Node.js：</span></div></div><div><div><div>7</div></div><div><span>nvm</span><span> </span><span>install</span><span> </span><span>--lts</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 验证 Node.js 版本：</span></div></div><div><div><div>10</div></div><div><span>node</span><span> </span><span>-v</span><span> </span><span># Should print "v24.16.0".</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span># 验证 npm 版本：</span></div></div><div><div><div>13</div></div><div><span>npm</span><span> </span><span>-v</span><span> </span><span># Should print "11.13.0".</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>1.2.2 Windows 安装 Node<a href="#122-windows-安装-node"><span>#</span></a></h4><p>对于 Windows，在<a href="https://nodejs.org/zh-cn/download" target="_blank">下载 Node.js</a>页面下方找到“或者获得适用于 Windows x64 平台的 Node.js 构建”，点击下载 Windows 安装程序 (.msi) 安装即可。</p></section><section><h4>1.2.3 安装 OpenCode<a href="#123-安装-opencode"><span>#</span></a></h4><p>Node.js 安装完成后（自带 npm），在终端中执行：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>opencode-ai</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>安装完成后，在任意终端输入 <code>opencode</code> 命令即可启动 TUI 界面的 OpenCode，同时会将当前所在文件夹作为工作目录。</p></section></section><section><h3>1.3 添加供应商<a href="#13-添加供应商"><span>#</span></a></h3><p>启动 OpenCode 后，按下 <code>Ctrl+P</code> 可以打开命令菜单，在其中找到 <strong>Connect provider</strong>，输入供应商名称（如 DeepSeek）即可搜索，选中后输入对应的 API Key 即可。</p><p>OpenCode 官方也提供了名为 OpenCode Zen 和 OpenCode Go 的大模型服务，并提供部分免费额度，可参考<a href="https://opencode.ai/docs/zh-cn/zen/#%E5%AE%9A%E4%BB%B7" target="_blank">OpenCode Zen 定价</a>。注册账号后将 API Key 添加到 OpenCode 中即可使用。</p></section></section>
<section><h2>2 桌面应用<a href="#2-桌面应用"><span>#</span></a></h2><p>OpenCode 官方提供了桌面应用，但目前功能较少，不推荐使用。推荐使用第三方的 <a href="https://openchamber.dev/zh/" target="_blank">OpenChamber</a>。</p><p>对于 macOS 和 Windows，可以直接下载其桌面应用。</p><p>对于 Linux，则需要安装 NPM 包后，在浏览器中以网页形式使用。</p><p>首先使用 NPM 安装 OpenChamber：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>i</span><span> </span><span>-g</span><span> </span><span>@openchamber/web</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>安装完成后，在终端中运行 <code>openchamber</code>，随后打开其输出的地址（通常为 <code>http://127.0.0.1:3000/</code>）即可进入 OpenChamber 的主界面。</p><p></p><figure><img alt="命令行启动 openchamber" loading="lazy" width="739" height="343" src="/_astro/image-20260615164504-5uqxboj.I65UgUby_Z1wXWDn.webp" /><figcaption>命令行启动 openchamber</figcaption></figure><p></p><p>进入主界面后，可以在设置中将语言切换为中文。</p><p></p><figure><img alt="openchamber 主界面" loading="lazy" width="1234" height="881" src="/_astro/image-20260615164622-ihpms46.Du6BhTLC_Z2m2tG5.webp" /><figcaption>openchamber 主界面</figcaption></figure><p></p></section>]]></content>
    </entry>
    <entry>
      <id>https://bill.is-a.dev/posts/gcmp-deepseek-vscode-copilot/</id>
      <title type="text">使用 GCMP 在 VS Code Copilot 中接入 DeepSeek API</title>
      <published>2026-06-11T00:00:00.000Z</published>
      <updated>2026-06-11T00:00:00.000Z</updated>
      <author><name>CMBill</name></author>
      <link rel="alternate" href="https://bill.is-a.dev/posts/gcmp-deepseek-vscode-copilot/"/>
      <summary type="text">介绍如何通过 GCMP 扩展在 VS Code Copilot Chat 中接入 DeepSeek API，实现使用国产大模型进行 AI 编程。</summary>
      <content type="html"><![CDATA[<p>GitHub Copilot 学生套餐一改再改，现如今删除了大部分强大的模型不说，一个月还只有相当于 2 美元的额度，基本已经属于不可用的程度了。现在也有越来越多的独立于编辑器的编码智能体，如 Codex、OpenCode 等，但是依旧可以利用插件，继续在 VSCode 里以习惯的方式使用其他供应商提供的模型（如 DeepSeek）。</p>
<section><h2>1 GCMP 是什么<a href="#1-gcmp-是什么"><span>#</span></a></h2><p>GCMP（GitHub Copilot Model Providers）是一个 VS Code 扩展，为 GitHub Copilot Chat 提供国产大模型支持。它内置了 DeepSeek、智谱 AI、MiniMax、MoonshotAI、阿里云百炼等主流国产模型，MIT 开源协议，免费使用。</p><ul>
<li>开源仓库：<a href="https://github.com/VicBilibily/GCMP" target="_blank">https://github.com/VicBilibily/GCMP</a></li>
<li>扩展 ID：<code>vicanent.gcmp</code></li>
</ul></section>
<section><h2>2 注册 DeepSeek 并获取 API Key<a href="#2-注册-deepseek-并获取-api-key"><span>#</span></a></h2><p>前往 <a href="https://platform.deepseek.com" target="_blank">DeepSeek 平台</a> 注册账号，在 API Keys 页面创建密钥并充值。</p></section>
<section><h2>3 安装 GCMP<a href="#3-安装-gcmp"><span>#</span></a></h2><p>在 VS Code 扩展市场中搜索 <code>GCMP</code> 或直接输入扩展 ID <code>vicanent.gcmp</code> 安装即可。</p><p></p><figure><img alt="image" loading="lazy" width="2558" height="1434" src="/_astro/image-20260611155758-uyn9zfu.o8UNCZsu_Z1w6cTo.webp" /><figcaption>image</figcaption></figure><p></p></section>
<section><h2>4 配置 DeepSeek 模型<a href="#4-配置-deepseek-模型"><span>#</span></a></h2><p>安装完成后按以下步骤操作：</p><ol>
<li>打开 VS Code 的 <strong>GitHub Copilot Chat</strong> 面板</li>
<li>点击聊天输入框下方的模型选择器</li>
<li>在底部齿轮选择<strong>管理语言模型</strong></li>
<li>点击右上角的<strong>添加模型</strong>，在弹出的提供商列表中选中 <strong>GCMP · DeepSeek</strong></li>
<li>首次选择会提示输入 API Key，粘贴上一步获取的密钥</li>
<li>返回模型选择器，勾选 <strong>DeepSeek-V4-Flash</strong> 或 <strong>DeepSeek-V4-Pro</strong> 启用</li>
</ol><p></p><figure><img alt="image" loading="lazy" width="2558" height="1434" src="/_astro/image-20260611160122-6f312oq.CpDNfO1a_ZTEWTy.webp" /><figcaption>image</figcaption></figure><p></p></section>
<section><h2>5 开始使用<a href="#5-开始使用"><span>#</span></a></h2><p>在 Copilot Chat 模型选择器中选中已启用的 DeepSeek 模型，即可像使用原版 Copilot 一样进行对话。状态栏会显示当前账户余额，方便随时查看。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://bill.is-a.dev/posts/cargo-install-notes/</id>
      <title type="text">cargo install 的一些杂项</title>
      <published>2026-05-17T00:00:00.000Z</published>
      <updated>2026-05-17T00:00:00.000Z</updated>
      <author><name>CMBill</name></author>
      <link rel="alternate" href="https://bill.is-a.dev/posts/cargo-install-notes/"/>
      <summary type="text">cargo install 的常用命令，以及 cargo-binstall 和 cargo-update 等辅助工具的用法。</summary>
      <content type="html"><![CDATA[<p><code>cargo install</code> 用于从 <a href="https://crates.io/" target="_blank">crates.io</a> 或指定的 Git 仓库下载 Rust crate 的源代码，并在本地编译后将其二进制文件安装到 <code>~/.cargo/bin</code> 目录中。</p>
<ul>
<li>
<p><strong>安装一个工具</strong>：</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cargo</span><span> </span><span>install</span><span> </span><span>ripgrep</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p><strong>安装特定版本</strong>：</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cargo</span><span> </span><span>install</span><span> </span><span>ripgrep@13.0.0</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p><strong>从 Git 仓库安装</strong>：</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cargo</span><span> </span><span>install</span><span> </span><span>--git</span><span> </span><span>https://github.com/BurntSushi/ripgrep</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p><strong>列出已安装的包</strong>：</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cargo</span><span> </span><span>install</span><span> </span><span>--list</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p><strong>卸载一个工具</strong>：</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cargo</span><span> </span><span>uninstall</span><span> </span><span>ripgrep</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul>
<blockquote><p><strong>注意</strong>：<code>cargo install</code> 默认会将二进制文件输出到 <code>~/.cargo/bin</code>，请确保该目录已添加到系统的 <code>PATH</code> 环境变量中。</p></blockquote>
<section><h2>1 <code>--locked</code> 参数<a href="#1---locked-参数"><span>#</span></a></h2><p><code>cargo install</code> 在默认情况下<strong>不会</strong>使用 crate 源码中可能自带的 <code>Cargo.lock</code> 文件，而是根据 <code>Cargo.toml</code> 中的依赖范围重新计算并<strong>取满足条件的最新依赖版本</strong>。这样做在某些情况下可能引入不兼容的改动，导致编译失败或运行时异常。</p><p>添加 <code>--locked</code> 参数可以改变这一行为：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cargo</span><span> </span><span>install</span><span> </span><span>--locked</span><span> </span><span>ripgrep</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其效果如下：</p><ul>
<li><strong>强制要求</strong>源码目录中存在 <code>Cargo.lock</code> 文件（若缺失，Cargo 会报错退出）。</li>
<li><strong>严格遵守</strong> <code>Cargo.lock</code> 中记录的每个依赖的具体版本，不再重新解析。</li>
</ul><p>这带来两点好处：</p><ol>
<li><strong>可重现的安装</strong>：安装结果与 crate 作者在开发及发布时所用的依赖环境完全一致。</li>
<li><strong>更高的稳定性</strong>：避免因某个依赖的小幅更新（即使语义上兼容）意外破坏工具功能。</li>
</ol><p>建议在安装绝大多数成熟的命令行工具（如 <code>ripgrep</code>、<code>fd</code>、<code>bat</code>）时都加上 <code>--locked</code>。</p></section>
<section><h2>2 使用 cargo-binstall 直接安装二进制文件<a href="#2-使用-cargo-binstall-直接安装二进制文件"><span>#</span></a></h2><p><code>cargo-binstall</code> 是一个旨在<strong>替代</strong> <code>cargo install</code> 的第三方工具。它通过<strong>下载预编译的二进制文件</strong>来代替本地编译，从而极大缩短安装时间。</p><section><h3>2.1 为什么使用它？<a href="#21-为什么使用它"><span>#</span></a></h3>

<table><thead><tr><th>特性</th><th><code>cargo binstall</code></th><th><code>cargo install</code></th></tr></thead><tbody><tr><td>安装方式</td><td>下载预编译二进制</td><td>下载源码并本地编译</td></tr><tr><td>速度</td><td>几秒内完成（尤其适合大型项目）</td><td>耗时较长，需要 Rust 工具链</td></tr><tr><td>依赖要求</td><td>不需要本地编译工具链</td><td>必须安装 Rust 工具链</td></tr></tbody></table></section><section><h3>2.2 如何使用<a href="#22-如何使用"><span>#</span></a></h3><p>首先，通过 <code>cargo install</code> 安装 <code>cargo-binstall</code> 自身（这一步仍需编译，但只需一次）：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cargo</span><span> </span><span>install</span><span> </span><span>cargo-binstall</span><span> </span><span>--locked</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>之后，即可使用 <code>cargo binstall</code> 安装其他工具：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cargo</span><span> </span><span>binstall</span><span> </span><span>ripgrep</span><span>        </span><span># 快速安装，自动获取预编译版本</span></div></div><div><div><div>2</div></div><div><span>cargo</span><span> </span><span>binstall</span><span> </span><span>tokei@12.1.0</span><span>   </span><span># 指定版本</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果 <code>cargo-binstall</code> 找不到预编译的二进制文件，它会<strong>自动回退</strong>到源码编译（即执行 <code>cargo install</code>），从而保证安装成功率。</p></section><section><h3>2.3 工作原理<a href="#23-工作原理"><span>#</span></a></h3><p><code>cargo binstall</code> 按以下顺序尝试获取二进制：</p><ol>
<li><strong>GitHub Releases</strong>：检查 crate 的 GitHub Release 页面是否附带了与当前平台匹配的预编译二进制。</li>
<li><strong>cargo-quickinstall</strong>：查询 <code>cargo-quickinstall</code> 提供的预编译包仓库。</li>
<li><strong>回退到源码安装</strong>：如果以上都失败，则执行 <code>cargo install</code>。</li>
</ol><p><code>cargo-binstall</code> 为 CI 环境、资源受限设备或追求效率的开发者提供了非常友好的体验。</p></section></section>
<section><h2>3 使用 cargo-update 更新<a href="#3-使用-cargo-update-更新"><span>#</span></a></h2><p><code>cargo install</code> 本身<strong>不提供</strong>内置的更新命令。当你需要将已安装的工具升级到最新版本时，可以使用第三方工具 <code>cargo-update</code>。</p><section><h3>3.1 安装 cargo-update<a href="#31-安装-cargo-update"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cargo</span><span> </span><span>install</span><span> </span><span>cargo-update</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.2 更新所有已安装的包<a href="#32-更新所有已安装的包"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cargo</span><span> </span><span>install-update</span><span> </span><span>-a</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>-a</code> 表示“全部”。该命令会检查每个通过 <code>cargo install</code> 安装的包，若发现新版本则自动升级。</p></section><section><h3>3.3 更新特定包<a href="#33-更新特定包"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cargo</span><span> </span><span>install-update</span><span> </span><span>ripgrep</span><span> </span><span>fd-find</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.4 注意事项<a href="#34-注意事项"><span>#</span></a></h3><ul>
<li><code>cargo-update</code> 会跳过那些不是通过 <code>cargo install</code> 安装的程序（例如系统包管理器安装的）。</li>
<li><code>cargo-update</code> 自身也可以自我更新：再次运行 <code>cargo install cargo-update</code> 或执行 <code>cargo install-update cargo-update</code>。</li>
</ul></section><section><h3>3.5 与 cargo binstall 的协作<a href="#35-与-cargo-binstall-的协作"><span>#</span></a></h3><ul>
<li><strong>安装/重装</strong>：优先使用 <code>cargo binstall</code> 达到快速部署。</li>
<li><strong>更新</strong>：仍然使用 <code>cargo install-update</code>，因为它会智能地处理版本检查，无论当初是通过 <code>cargo install</code> 还是 <code>cargo binstall</code> 安装的，都能正确升级（前提是后续更新时依然能获取到二进制或源码）。</li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://bill.is-a.dev/posts/colcon-build-parameters/</id>
      <title type="text">常用的 colcon build 参数</title>
      <published>2026-05-17T00:00:00.000Z</published>
      <updated>2026-05-17T00:00:00.000Z</updated>
      <author><name>CMBill</name></author>
      <link rel="alternate" href="https://bill.is-a.dev/posts/colcon-build-parameters/"/>
      <summary type="text">colcon build 是 ROS 2 中用来构建工作空间的核心命令，本文按用途分类介绍最常用的参数。</summary>
      <content type="html"><![CDATA[<p><code>colcon build</code> 是 ROS 2 中用来构建工作空间的核心命令。下面按用途分类，介绍最常用的一些参数。</p>
<section><h2>1 安装方式<a href="#1-安装方式"><span>#</span></a></h2><ul>
<li><strong><code>--symlink-install</code></strong><br />
使用符号链接安装文件，而不是复制。当你修改了 Python 脚本、配置文件或 launch 文件时，<strong>无需重新构建</strong>就能直接生效（对 Python 包、配置文件、launch 文件这类“改动频繁”的内容特别方便，但 C++ 代码仍需重新编译）。开发调试时常用。</li>
<li><strong><code>--merge-install</code></strong><br />
将所有包安装到同一个 <code>install/</code> 目录下（类似 ROS 1 的结构），而不是默认的 <code>install/&lt;pkg_name&gt;</code>。某些场景下可以简化环境变量设置，不过通常推荐独立安装方式。</li>
</ul></section>
<section><h2>2 包选择<a href="#2-包选择"><span>#</span></a></h2><ul>
<li><strong><code>--packages-select</code></strong><br />
只构建指定的几个包，不处理其他包。</li>
<li><strong><code>--packages-up-to</code></strong><br />
构建指定包以及它的所有递归依赖包。适合你只关心某个包和它依赖的包链。</li>
<li><strong><code>--packages-above</code></strong><br />
构建所有直接或间接依赖于指定包的包（反向依赖）。用于检查改动影响哪些下游包。</li>
<li><strong><code>--packages-skip</code></strong><br />
跳过指定包不构建，但其他包仍正常构建。</li>
</ul></section>
<section><h2>3 构建过程控制<a href="#3-构建过程控制"><span>#</span></a></h2><ul>
<li><strong><code>--continue-on-error</code></strong><br />
即便某个包构建失败，也继续构建其他包，便于一次性发现问题包。</li>
<li><strong><code>--executor sequential</code></strong><br />
强制顺序构建（默认会并行构建），方便调试依赖问题或排查编译顺序。</li>
<li><strong><code>--parallel-workers N</code></strong><br />
限制并行构建的任务数，避免资源紧张，例如 <code>--parallel-workers 4</code>。</li>
<li><strong><code>--cmake-args</code></strong><br />
将参数直接传递给 CMake，如：<br />
<code>--cmake-args -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O2"</code></li>
<li><strong><code>--ament-cmake-args</code></strong><br />
将参数传递给 <code>ament_cmake</code> 构建系统（ROS 2 专用），用法同上。</li>
<li><strong><code>--cmake-clean-cache</code></strong><br />
删除之前的 CMake 缓存，重新从头配置，相当于强制 clean 配置。</li>
<li><strong><code>--cmake-force-configure</code></strong><br />
即使缓存存在，也强制重新运行 CMake 配置步骤。</li>
<li><strong><code>--allow-overriding</code></strong><br />
改变某个包的构建类型（例如把一个原本用 <code>ament_cmake</code> 的包强制当成纯 <code>cmake</code> 包来构建），在混用不同构建系统时有用。</li>
</ul></section>
<section><h2>4 输出与事件处理<a href="#4-输出与事件处理"><span>#</span></a></h2><ul>
<li>
<p><strong><code>--event-handlers</code></strong><br />
控制终端输出和通知行为。常用值：</p>
<ul>
<li><code>console_direct+</code>：直接在终端实时打印所有包的编译输出（不缓冲）。</li>
<li><code>console_cohesion+</code>：每个包输出聚合显示（默认之一）。</li>
<li><code>desktop_notification-</code>：关闭桌面通知（构建完成/失败的通知）。<br />
示例：<code>--event-handlers console_direct+</code></li>
</ul>
</li>
</ul></section>
<section><h2>5 测试相关<a href="#5-测试相关"><span>#</span></a></h2><ul>
<li><strong><code>--build-tests</code></strong><br />
在构建过程中同时编译测试代码，后续才能运行 <code>colcon test</code>。<br />
也可以直接用 CMake 参数开启：<code>--cmake-args -DBUILD_TESTING=ON</code>。</li>
</ul></section>
<section><h2>6 工作空间路径<a href="#6-工作空间路径"><span>#</span></a></h2><ul>
<li><strong><code>--build-base</code></strong><br />
指定构建中间文件存放目录，默认为工作空间下的 <code>build/</code>。</li>
<li><strong><code>--install-base</code></strong><br />
指定最终安装文件的存放目录，默认为 <code>install/</code>。</li>
<li><strong><code>--paths</code></strong><br />
指定包含 ROS 包的源代码目录，默认为 <code>src/</code>，如果你的包放在别处可以用它指定。</li>
</ul></section>
<section><h2>7 其他<a href="#7-其他"><span>#</span></a></h2><ul>
<li><strong><code>--metas</code></strong><br />
使用一个元信息文件（meta file）来统一覆盖某些包的编译选项，适合复杂的混合工作空间。</li>
</ul></section>
<section><h2>8 常见场景示例<a href="#8-常见场景示例"><span>#</span></a></h2><ul>
<li><strong>日常开发最常用</strong><br />
<code>colcon build --symlink-install --packages-select my_package</code></li>
<li><strong>重新全量构建（清除缓存）</strong><br />
<code>colcon build --cmake-clean-cache --cmake-force-configure</code></li>
<li><strong>只构建某个包及其依赖，并实时查看输出</strong><br />
<code>colcon build --packages-up-to my_package --event-handlers console_direct+</code></li>
<li><strong>以 Release 模式编译</strong><br />
<code>colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release</code></li>
</ul><p>掌握这些参数基本就能覆盖绝大部分开发和集成场景了。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://bill.is-a.dev/posts/git-submodule-commands/</id>
      <title type="text">Git Submodule 命令</title>
      <published>2026-05-17T00:00:00.000Z</published>
      <updated>2026-05-17T00:00:00.000Z</updated>
      <author><name>CMBill</name></author>
      <link rel="alternate" href="https://bill.is-a.dev/posts/git-submodule-commands/"/>
      <summary type="text">Git Submodule 常用命令与配置详解，包括添加、克隆、更新、删除等操作及配置文件说明。</summary>
      <content type="html"><![CDATA[<p><code>git submodule</code> 是用于在一个仓库中嵌套管理另一个独立 Git 仓库的工具。主仓库仅记录子模块所处的具体提交（commit），而不直接追踪子模块中的文件内容。</p>
<section><h2>1 常用命令<a href="#1-常用命令"><span>#</span></a></h2><section><h3>1.1 添加子模块<a href="#11-添加子模块"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>add</span><span> </span><span>&lt;仓库URL&gt;</span><span> </span><span>&lt;本地路径&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>执行后会生成 <code>.gitmodules</code> 文件，并记录子模块当前所在的 commit。</p></section><section><h3>1.2 克隆含有子模块的仓库<a href="#12-克隆含有子模块的仓库"><span>#</span></a></h3><ul>
<li>
<p>一步到位克隆：</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>clone</span><span> </span><span>--recurse-submodules</span><span> </span><span>&lt;主仓库URL&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>已克隆但子模块为空时，手动初始化并更新：</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>update</span><span> </span><span>--init</span><span> </span><span>--recursive</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul><p>此处的 <code>--init</code> 参数相当于先执行 <code>init</code> 子命令后再执行 <code>update</code> 子命令，<code>git submodule init</code> 会根据 <code>.gitmodules</code> 文件的信息初始化本地 Git 配置（<code>.git/config</code>）。</p></section><section><h3>1.3 更新子模块<a href="#13-更新子模块"><span>#</span></a></h3><ul>
<li>
<p>将子模块更新到远程仓库的最新提交：</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>update</span><span> </span><span>--remote</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>更新后，主仓库中记录的子模块指针会变化，需要手动提交：</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>add</span><span> </span><span>&lt;子模块路径&gt;</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>commit</span><span> </span><span>-m</span><span> </span><span>"更新子模块到最新版本"</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ul></section><section><h3>1.4 查看子模块状态<a href="#14-查看子模块状态"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>status</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>输出中前缀含义：</p><ul>
<li><code>-</code> 表示未初始化</li>
<li><code>+</code> 表示当前检出的提交与主仓库记录的提交不一致</li>
</ul></section><section><h3>1.5 删除子模块<a href="#15-删除子模块"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>deinit</span><span> </span><span>&lt;路径&gt;</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>rm</span><span> </span><span>&lt;路径&gt;</span></div></div><div><div><div>3</div></div><div><span>rm</span><span> </span><span>-rf</span><span> </span><span>.git/modules/&lt;路径&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>最后提交删除操作。</p></section><section><h3>1.6 同步子模块 URL<a href="#16-同步子模块-url"><span>#</span></a></h3><p><code>sync</code> 子命令用于将 <code>.gitmodules</code> 文件中定义的远程 URL 同步到本地 Git 配置（<code>.git/config</code>）中。</p><section><h4>1.6.1 何时需要<a href="#161-何时需要"><span>#</span></a></h4><p>当子模块的远程仓库地址发生变更（例如迁移到新服务器）时，仅修改 <code>.gitmodules</code> 是不够的。本地的 <code>.git/config</code> 仍保留旧地址，导致 <code>git submodule update</code> 等命令访问错误。此时需运行 <code>sync</code> 使本地配置与 <code>.gitmodules</code> 一致。</p></section><section><h4>1.6.2 典型场景<a href="#162-典型场景"><span>#</span></a></h4><ol>
<li>子模块远程库从 <code>github.com/old/repo</code> 迁至 <code>gitlab.com/new/repo</code>。</li>
<li>更新 <code>.gitmodules</code> 中的 URL 并提交。</li>
<li>其他成员拉取主仓库后，执行 <code>git submodule sync</code>，否则更新时会尝试从旧地址拉取而失败。</li>
</ol></section><section><h4>1.6.3 用法<a href="#163-用法"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 同步所有子模块</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>sync</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 同步指定子模块</span></div></div><div><div><div>5</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>sync</span><span> </span><span>&lt;子模块路径&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 递归同步（子模块嵌套时）</span></div></div><div><div><div>8</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>sync</span><span> </span><span>--recursive</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section>
<section><h2>2 子模块在仓库中的配置文件<a href="#2-子模块在仓库中的配置文件"><span>#</span></a></h2><section><h3>2.1 <code>.gitmodules</code>：版本控制的配置文件<a href="#21-gitmodules版本控制的配置文件"><span>#</span></a></h3><p>位于主仓库根目录，采用 INI 格式，记录了子模块的逻辑路径和远程 URL。<strong>该文件随主仓库一起版本控制</strong>，他人克隆后可见。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[</span><span>submodule "libs/mylib"</span><span>]</span></div></div><div><div><div>2</div></div><div><span>    </span><span>path</span><span> </span><span>=</span><span> libs/mylib</span></div></div><div><div><div>3</div></div><div><span>    </span><span>url</span><span> </span><span>=</span><span> https://github.com/example/lib.git</span></div></div><div><div><div>4</div></div><div><span>[</span><span>submodule "vendor/other"</span><span>]</span></div></div><div><div><div>5</div></div><div><span>    </span><span>path</span><span> </span><span>=</span><span> vendor/other</span></div></div><div><div><div>6</div></div><div><span>    </span><span>url</span><span> </span><span>=</span><span> git@gitlab.com:team/other.git</span></div></div><div><div><div>7</div></div><div><span>    </span><span>branch</span><span> </span><span>=</span><span> develop</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li><code>path</code>：子模块在主仓库中的相对路径。</li>
<li><code>url</code>：子模块的远程仓库地址。</li>
<li>可选字段如 <code>branch</code>，指定 <code>git submodule update --remote</code> 时跟踪的分支（不写则默认跟踪主仓库记录的 commit）。</li>
</ul></section><section><h3>2.2 <code>.git/config</code>：本地仓库配置（不版本控制）<a href="#22-gitconfig本地仓库配置不版本控制"><span>#</span></a></h3><p>执行 <code>git submodule init</code> 后，<code>.gitmodules</code> 中的配置会被复制到 <code>.git/config</code> 中，形成类似结构：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[</span><span>submodule "libs/mylib"</span><span>]</span></div></div><div><div><div>2</div></div><div><span>    </span><span>url</span><span> </span><span>=</span><span> https://github.com/example/lib.git</span></div></div><div><div><div>3</div></div><div><span>    </span><span>active</span><span> </span><span>=</span><span> true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>后续 <code>git submodule update</code> 等命令实际读取的是此处的 <code>url</code>。</li>
<li>若 <code>.gitmodules</code> 中的 URL 变化，需手动执行 <code>git submodule sync</code> 来同步此处。</li>
<li>该文件<strong>仅在本地存在，不会被提交</strong>，每个人的副本独立。</li>
</ul></section><section><h3>2.3 主仓库的树对象：子模块指针的存储<a href="#23-主仓库的树对象子模块指针的存储"><span>#</span></a></h3><p>子模块本质上是一个类型为 commit 的 Git 对象，它在主仓库每一次提交的树对象中表现为一个特殊条目：</p><ul>
<li>模式：<code>160000</code>（普通文件是 <code>100644</code>，目录是 <code>040000</code>）。</li>
<li>名称：子模块的目录名。</li>
<li>哈希：指向子模块仓库中某一个具体 commit 的 SHA-1 值。</li>
</ul><p>用底层命令可查看到类似：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>$</span><span> </span><span>git</span><span> </span><span>ls-tree</span><span> </span><span>HEAD</span><span> </span><span>libs/mylib</span></div></div><div><div><div>2</div></div><div><span>160000</span><span> </span><span>commit</span><span> </span><span>1a2b3c4d...</span><span>  </span><span>libs/mylib</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这个 commit SHA 决定了子模块检出到哪个版本。当你在主仓库中 <code>git add libs/mylib</code> 并提交时，实际上就是更新了这个哈希值。</p></section><section><h3>2.4 <code>.git/modules/&lt;路径&gt;</code>：子模块实际的 Git 数据<a href="#24-gitmodules路径子模块实际的-git-数据"><span>#</span></a></h3><p>子模块本身的仓库（对象库、引用等）存储在主仓库的 <code>.git/modules/&lt;子模块路径&gt;</code> 目录下（旧版本 Git 直接存放在子模块目录内的 <code>.git</code> 文件中）。<br />
这个目录对用户一般是透明的，但它是子模块能独立进行 Git 操作的基础。</p></section></section>
<section><h2>3 一些操作<a href="#3-一些操作"><span>#</span></a></h2><section><h3>3.1 将子文件夹独立为子模块<a href="#31-将子文件夹独立为子模块"><span>#</span></a></h3><p>假设原仓库结构：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>my-project/</span></div></div><div><div><div>2</div></div><div><span>├── .git/</span></div></div><div><div><div>3</div></div><div><span>├── target-folder/       ← 想独立为子模块的文件夹</span></div></div><div><div><div>4</div></div><div><span>│   ├── a.txt</span></div></div><div><div><div>5</div></div><div><span>│   └── b.js</span></div></div><div><div><div>6</div></div><div><span>└── other-stuff/</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>最终：<code>target-folder</code> 变成一个独立的 Git 仓库，并在原仓库中作为子模块引用，且所有历史提交（仅针对 <code>target-folder/</code> 的修改）都保留在新仓库里。</p><section><h4>3.1.1 保留子文件夹提交历史<a href="#311-保留子文件夹提交历史"><span>#</span></a></h4><ol>
<li>
<p>提取文件夹的历史到一个新分支</p>
<p>进入原仓库根目录，运行：</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>subtree</span><span> </span><span>split</span><span> </span><span>--prefix=target-folder</span><span> </span><span>--branch=subrepo-branch</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>这会创建一个名为 <code>subrepo-branch</code> 的新分支，其中只包含 <code>target-folder/</code> 的历史记录（就像这个文件夹一直是独立仓库一样）。</p>
</li>
<li>
<p>创建一个新的 Git 仓库并推送</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 创建临时目录并初始化新仓库</span></div></div><div><div><div>2</div></div><div><span>mkdir</span><span> </span><span>/tmp/new-subrepo</span></div></div><div><div><div>3</div></div><div><span>cd</span><span> </span><span>/tmp/new-subrepo</span></div></div><div><div><div>4</div></div><div><span>git</span><span> </span><span>init</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span># 拉取刚才 split 出来的历史</span></div></div><div><div><div>7</div></div><div><span>git</span><span> </span><span>pull</span><span> </span><span>/path/to/my-project</span><span> </span><span>subrepo-branch</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 添加远程仓库（例如 GitHub 上新创建的空仓库）</span></div></div><div><div><div>10</div></div><div><span>git</span><span> </span><span>remote</span><span> </span><span>add</span><span> </span><span>origin</span><span> </span><span>&lt;新仓库的远程地址&gt;</span></div></div><div><div><div>11</div></div><div><span>git</span><span> </span><span>push</span><span> </span><span>-u</span><span> </span><span>origin</span><span> </span><span>master</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>回到原仓库，删除原文件夹并添加子模块</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cd</span><span> </span><span>/path/to/my-project</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span># 删除原文件夹（注意先确保新仓库已推送完成）</span></div></div><div><div><div>4</div></div><div><span>git</span><span> </span><span>rm</span><span> </span><span>-rf</span><span> </span><span>target-folder</span></div></div><div><div><div>5</div></div><div><span>git</span><span> </span><span>commit</span><span> </span><span>-m</span><span> </span><span>"Remove target-folder to replace with submodule"</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 添加子模块，使用新仓库的地址</span></div></div><div><div><div>8</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>add</span><span> </span><span>&lt;新仓库的远程地址&gt;</span><span> </span><span>target-folder</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 提交子模块配置</span></div></div><div><div><div>11</div></div><div><span>git</span><span> </span><span>commit</span><span> </span><span>-m</span><span> </span><span>"Add target-folder as submodule"</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>清理临时分支</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>branch</span><span> </span><span>-D</span><span> </span><span>subrepo-branch</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ol></section><section><h4>3.1.2 不保留子文件夹提交历史<a href="#312-不保留子文件夹提交历史"><span>#</span></a></h4><p>如果你不需要保留 <code>target-folder</code> 在原仓库中的提交历史，可以直接：</p><ol>
<li>把 <code>target-folder</code> 的内容复制到一个全新的 Git 仓库并推送。</li>
<li>在原仓库中删除 <code>target-folder</code>（<code>git rm -rf target-folder</code>）。</li>
<li><code>git submodule add &lt;新仓库地址&gt; target-folder</code>。</li>
<li>提交。</li>
</ol><p>历史会丢失（新仓库只有一次初始提交），但操作更快。</p></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://bill.is-a.dev/posts/manage-node-with-pnpm/</id>
      <title type="text">使用 pnpm 管理 node 运行环境</title>
      <published>2026-05-17T00:00:00.000Z</published>
      <updated>2026-05-17T00:00:00.000Z</updated>
      <author><name>CMBill</name></author>
      <link rel="alternate" href="https://bill.is-a.dev/posts/manage-node-with-pnpm/"/>
      <summary type="text">使用 pnpm 的 runtime 命令管理 Node.js 运行环境，包括安装不同版本和项目级配置说明。</summary>
      <content type="html"><![CDATA[<p>参考 <a href="https://pnpm.io/zh/installation" target="_blank">安装 | pnpm</a>、<a href="https://pnpm.io/zh/cli/runtime" target="_blank">pnpm runtime  | pnpm</a></p>
<p>在 pnpm v6 开始可以使用 <code>pnpm env</code> 命令管理 Node 环境，类似于 nvm 等工具，而在 v11 开始变为 <code>pnpm runtime</code> 命令。因此，可以使用独立脚本在不安装 Node 的情况下直接安装 pnpm，然后来管理计算机上的 Node 环境。</p>
<section><h2>1 基本用法<a href="#1-基本用法"><span>#</span></a></h2><p>使用 <code>pnpm runtime set</code> 命令可以方便地安装各种版本：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>pnpm</span><span> </span><span>runtime</span><span> </span><span>set</span><span> </span><span>&lt;name&gt;</span><span> </span><span>&lt;version&gt;</span><span> [-g]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>支持的运行时包括：</p><ul>
<li><code>node</code> - Node.js</li>
<li><code>deno</code> - Deno</li>
<li><code>bun</code> - Bun</li>
</ul><p>例如：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装最新的 Node.js v22 长期支持（LTS）版本</span></div></div><div><div><div>2</div></div><div><span>pnpm</span><span> </span><span>runtime</span><span> </span><span>set</span><span> </span><span>node</span><span> </span><span>lts</span><span> </span><span>-g</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 安装最新版本</span></div></div><div><div><div>5</div></div><div><span>pnpm</span><span> </span><span>runtime</span><span> </span><span>set</span><span> </span><span>node</span><span> </span><span>latest</span><span> </span><span>-g</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 安装指定版本号</span></div></div><div><div><div>8</div></div><div><span>pnpm</span><span> </span><span>runtime</span><span> </span><span>set</span><span> </span><span>node</span><span> </span><span>22.11.0</span><span> </span><span>-g</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 安装预发布版 (Release Candidate)</span></div></div><div><div><div>11</div></div><div><span>pnpm</span><span> </span><span>runtime</span><span> </span><span>set</span><span> </span><span>node</span><span> </span><span>rc</span><span> </span><span>-g</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span># 通过代号安装，例如 Node.js 4.x 的代号 'argon'</span></div></div><div><div><div>14</div></div><div><span>pnpm</span><span> </span><span>runtime</span><span> </span><span>set</span><span> </span><span>node</span><span> </span><span>argon</span><span> </span><span>-g</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><p><strong>重要提示</strong>：自 v11 起，通过 <code>pnpm runtime</code> 安装的 Node.js 将<strong>不再包含</strong>捆绑的 <code>npm</code>、<code>npx</code> 和 <code>corepack</code>。这可以显著减少安装时的文件数量、提升速度。如果需要，你仍然可以通过 <code>pnpm add -g npm</code> 来单独安装 <code>npm</code>。</p></blockquote></section>
<section><h2>2 项目级配置：<code>devEngines.runtime</code><a href="#2-项目级配置devenginesruntime"><span>#</span></a></h2><p>为了让团队使用统一的 Node.js 版本，可以将运行时要求写入项目的 <code>package.json</code> 文件中。这比在命令行手动切换更可靠。</p><p>在 <code>package.json</code> 中添加 <code>devEngines.runtime</code> 字段。例如，声明你的项目需要 Node.js v24.4.0 或更高版本：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>"devEngines": {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>"runtime": {</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>"name": "node",</span></div></div><div><div><div>5</div></div><div><span><span>      </span></span><span>"version": "^24.4.0",</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>"onFail": "download"</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这里的配置项含义如下：</p><ul>
<li><strong>name</strong>：<code>"node"</code>，表示声明的是 Node.js 运行时。也支持 <code>"deno"</code> 或 <code>"bun"</code>。</li>
<li><strong>version</strong>：<code>"^24.4.0"</code>，一个 <a href="https://semver.org/lang/zh-CN/" target="_blank">semver</a> 版本范围。<code>pnpm install</code> 时会自动解析并安装符合该范围的最新版本，并将确切版本锁定在 <code>pnpm-lock.yaml</code> 中。</li>
<li><strong>onFail</strong>：<code>"download"</code>，定义了当本地环境不满足版本要求时的行为。可选值有 <code>"download"</code>（自动下载）、<code>"warn"</code>（仅警告）、<code>"error"</code>（报错）或 <code>"ignore"</code>（忽略）。</li>
</ul><p>配置后，只需运行 <code>pnpm install</code>，pnpm 就会根据配置自动处理 Node.js 的安装和校验。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://bill.is-a.dev/posts/neovim-configuration/</id>
      <title type="text">Neovim 配置</title>
      <published>2026-05-17T00:00:00.000Z</published>
      <updated>2026-05-17T00:00:00.000Z</updated>
      <author><name>CMBill</name></author>
      <link rel="alternate" href="https://bill.is-a.dev/posts/neovim-configuration/"/>
      <summary type="text">Neovim 的配置指南，包括配置文件位置、将 vim 链接到 nvim 的方法，以及使用 lazy.nvim 管理插件的详细配置。</summary>
      <content type="html"><![CDATA[<p>使用 Vim 的分支 <a href="https://neovim.io/" target="_blank">Neovim</a>，与 Vim 不同，Neovim 的用户配置文件位于 <code>$XDG_CONFIG_HOME/nvim/</code>（默认是 <code>~/.config/nvim/</code>），全局配置文件（默认不存在）位于 <code>$XDG_CONFIG_DIRS/nvim/sysinit.vim</code>（默认是 <code>/etc/xdg/nvim/sysinit.vim</code>），如果不存在，则会载入 <code>/usr/share/nvim/sysinit.vim</code>，后者不建议由用户编辑。同时，Neovim 的配置文件支持 Lua 语法。</p>
<p>Windows 下配置文件位于 <code>C:\Users\Bill\AppData\Local\nvim\init.vim</code>。</p>
<section><h2>1 将 vim 链接到 nvim<a href="#1-将-vim-链接到-nvim"><span>#</span></a></h2><p>在 Arch Linux 下，可以使用包 <a href="https://aur.archlinux.org/packages/neovim-symlinks" target="_blank">AUR (zh_CN) - neovim-symlinks</a> 来办到。</p><p>在 Linux 下，使用软链接：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>mkdir</span><span> </span><span>-p</span><span> </span><span>~/.local/bin</span><span>  </span><span># 创建用户级别的 bin 目录</span></div></div><div><div><div>2</div></div><div><span>ln</span><span> </span><span>-s</span><span> </span><span>$(</span><span>which</span><span> </span><span>nvim</span><span>)</span><span> </span><span>~/.local/bin/vim</span><span>  </span><span># 建立软链接</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>随后将自定义目录添加到 <code>PATH</code> 开头，在 <code>~/.bashrc</code>（或 <code>~/.zshrc</code>）中添加：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> PATH</span><span>=</span><span>"</span><span>$HOME</span><span>/.local/bin:</span><span>$PATH</span><span>"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在 Powershell 中，使用别名：</p><p>单次配置：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>Set-Alias</span><span> </span><span>-</span><span>Name vim </span><span>-</span><span>Value nvim</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>使用 <code>echo $PROFILE</code> 命令找到配置文件位置，随后在对应的目录下建立对应的配置文件，并在其中写入：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>vim</span><span> </span><span>{</span><span>nvim</span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>2 配置文件及插件管理器<a href="#2-配置文件及插件管理器"><span>#</span></a></h2><p>使用插件管理器 <a href="https://github.com/folke/lazy.nvim" target="_blank">folke/lazy.nvim</a>，通过直接编辑 Neovim 的配置文件即可引入。同时，有一个名为 <a href="https://github.com/LazyVim/LazyVim" target="_blank">LazyVim/LazyVim</a> 的项目，提供了开箱即用的配置模板，功能强大，但是对于我而言不需要这么多功能，所以只选择了部分插件。Lazy nvim 直接通过编辑配置文件的方式引入，整个配置文件夹结构为：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>nvim</span></div></div><div><div><div>2</div></div><div><span>├──</span><span> </span><span>init.lua</span></div></div><div><div><div>3</div></div><div><span>├──</span><span> </span><span>lazy-lock.json</span></div></div><div><div><div>4</div></div><div><span>└──</span><span> </span><span>lua</span></div></div><div><div><div>5</div></div><div><span>    </span><span>├──</span><span> </span><span>config</span></div></div><div><div><div>6</div></div><div><span>    </span><span>│  </span><span> </span><span>└──</span><span> </span><span>lazy.lua</span></div></div><div><div><div>7</div></div><div><span>    </span><span>└──</span><span> </span><span>plugins</span></div></div><div><div><div>8</div></div><div><span>        </span><span>└──</span><span> </span><span>spec1.lua</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>具体的配置文件，可以参考我的 dotfile 仓库：<a href="https://github.com/CMBill/dotfiles/tree/master/dotfiles/nvim" target="_blank">dotfiles/dotfiles/nvim at master · CMBill/dotfiles</a></p><p>要安装 Lazy nvim，首先在 <code>~/.config/nvim/init.lua</code> 文件中引入：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>require</span><span>(</span><span>"config.lazy"</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>随后在配置文件夹下新建 <code>lua/config</code> 文件夹，在 <code>~/.config/nvim/lua/config/lazy.lua</code> 中填写 lazy.nvim 启动文件：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>-- Bootstrap lazy.nvim</span></div></div><div><div><div>2</div></div><div><span>local</span><span> lazypath </span><span>=</span><span> vim.fn.</span><span>stdpath</span><span>(</span><span>"data"</span><span>) </span><span>..</span><span> </span><span>"/lazy/lazy.nvim"</span></div></div><div><div><div>3</div></div><div><span>if</span><span> </span><span>not</span><span> (vim.uv </span><span>or</span><span> vim.loop).</span><span>fs_stat</span><span>(lazypath) </span><span>then</span></div></div><div><div><div>4</div></div><div><span>  </span><span>local</span><span> lazyrepo </span><span>=</span><span> </span><span>"https://github.com/folke/lazy.nvim.git"</span></div></div><div><div><div>5</div></div><div><span>  </span><span>local</span><span> out </span><span>=</span><span> vim.fn.</span><span>system</span><span>({ </span><span>"git"</span><span>, </span><span>"clone"</span><span>, </span><span>"--filter=blob:none"</span><span>, </span><span>"--branch=stable"</span><span>, lazyrepo, lazypath })</span></div></div><div><div><div>6</div></div><div><span>  </span><span>if</span><span> vim.v.shell_error </span><span>~=</span><span> </span><span>0</span><span> </span><span>then</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>vim.api.</span><span>nvim_echo</span><span>({</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>{ </span><span>"Failed to clone lazy.nvim:</span><span>\n</span><span>"</span><span>, </span><span>"ErrorMsg" </span><span>},</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>{ out, </span><span>"WarningMsg" </span><span>},</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>{ </span><span>"</span><span>\n</span><span>Press any key to exit..." </span><span>},</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}, </span><span>true</span><span>, {})</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>vim.fn.</span><span>getchar</span><span>()</span></div></div><div><div><div>13</div></div><div><span>    </span><span>os.exit</span><span>(</span><span>1</span><span>)</span></div></div><div><div><div>14</div></div><div><span>  </span><span>end</span></div></div><div><div><div>15</div></div><div><span>end</span></div></div><div><div><div>16</div></div><div><span>vim.opt.</span><span>rtp</span><span>:</span><span>prepend</span><span>(lazypath)</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>-- Make sure to setup `mapleader` and `maplocalleader` before</span></div></div><div><div><div>19</div></div><div><span>-- loading lazy.nvim so that mappings are correct.</span></div></div><div><div><div>20</div></div><div><span>-- This is also a good place to setup other settings (vim.opt)</span></div></div><div><div><div>21</div></div><div><span>vim.g.mapleader </span><span>=</span><span> </span><span>" "</span></div></div><div><div><div>22</div></div><div><span>vim.g.maplocalleader </span><span>=</span><span> </span><span>"</span><span>\\</span><span>"</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>-- Setup lazy.nvim</span></div></div><div><div><div>25</div></div><div><span>require</span><span>(</span><span>"lazy"</span><span>).</span><span>setup</span><span>({</span></div></div><div><div><div>26</div></div><div><span><span>  </span></span><span>spec </span><span>=</span><span> {</span></div></div><div><div><div>27</div></div><div><span>    </span><span>-- import your plugins</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>{ import </span><span>=</span><span> </span><span>"plugins" </span><span>},</span></div></div><div><div><div>29</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>30</div></div><div><span>  </span><span>-- Configure any other settings here. See the documentation for more details.</span></div></div><div><div><div>31</div></div><div><span>  </span><span>-- colorscheme that will be used when installing plugins.</span></div></div><div><div><div>32</div></div><div><span><span>  </span></span><span>install </span><span>=</span><span> { colorscheme </span><span>=</span><span> { </span><span>"habamax" </span><span>} },</span></div></div><div><div><div>33</div></div><div><span>  </span><span>-- automatically check for plugin updates</span></div></div><div><div><div>34</div></div><div><span><span>  </span></span><span>checker </span><span>=</span><span> { enabled </span><span>=</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>35</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>要安装、配置插件，则在 <code>lua</code> 文件夹里新建 <code>plugin</code> 文件夹，在其中的 <code>lua</code> 文件会被识别为插件配置。这里是我的插件配置：</p><ol>
<li>
<p><strong>主题：catppuccin</strong><br />
配置首先加载了 <code>catppuccin/nvim</code>，并设置为高优先级（1000），确保界面尽早渲染。配色选用柔和的 <code>mocha</code> 风格，加载后直接应用，为整个编辑器定下温暖、护眼的基调。</p>
</li>
<li>
<p><strong>功能中心：snacks.nvim</strong><br />
<code>folke/snacks.nvim</code> 是本配置的核心，它集成了十几项日常高频功能，且默认全部开启：大文件优化、仪表盘、文件浏览器、图片预览、缩进线、输入增强、通知系统、智能选择器、快速文件跳转、作用域高亮、平滑滚动、状态列和单词跳转等。通知超时设为 3 秒。</p>
<p>快捷键设计非常有条理，几乎覆盖所有场景：</p>
<ul>
<li><strong>查找与导航</strong>：<code>&lt;leader&gt;ff</code> 查找文件，<code>&lt;leader&gt;fg</code> 查找 Git 文件，<code>&lt;leader&gt;/</code> 全文搜索，<code>&lt;leader&gt;fb</code> 切换缓冲区，<code>&lt;leader&gt;fp</code> 查找项目，<code>&lt;leader&gt;fr</code> 最近文件。</li>
<li><strong>Git 操作</strong>：直接使用 <code>gs</code>/<code>gl</code>/<code>gd</code> 等组合查看状态、日志、差异、暂存等，<code>&lt;leader&gt;gg</code> 打开 Lazygit，<code>&lt;leader&gt;gB</code> 在浏览器中打开远程仓库。</li>
<li><strong>LSP 集成</strong>：<code>gd</code> 跳转定义，<code>gr</code> 查看引用，<code>gy</code> 类型定义，<code>&lt;leader&gt;ss</code> 工作区符号等，完全取代原生 LSP 浮窗，界面更统一。</li>
<li><strong>实用工具</strong>：<code>&lt;leader&gt;e</code> 打开文件浏览器，<code>&lt;leader&gt;z</code> 进入禅模式，<code>&lt;leader&gt;.</code> 打开草稿缓冲区，<code>&lt;c-/&gt;</code> 切换终端，<code>&lt;leader&gt;bd</code> 删除缓冲区，<code>&lt;leader&gt;cR</code> 重命名文件。</li>
</ul>
<p><code>init</code> 函数中还准备了调试辅助（<code>dd</code>、<code>bt</code>）和一系列 <code>toggle</code> 映射，比如 <code>&lt;leader&gt;us</code> 切换拼写检查、<code>&lt;leader&gt;ul</code> 切换行号、<code>&lt;leader&gt;ud</code> 切换诊断显示等，一键控制编辑器状态，非常方便。</p>
</li>
<li>
<p><strong>按键提示：which-key.nvim</strong><br />
配置了 <code>folke/which-key.nvim</code>，采用 <code>helix</code> 风格预设，在按下 <code>&lt;leader&gt;</code> 后自动弹出可用的快捷键菜单。额外添加了 <code>&lt;leader&gt;?</code> 手动显示当前缓冲区专属快捷键，帮助记忆不常用的组合键。</p>
</li>
<li>
<p><strong>状态栏：lualine.nvim</strong><br />
<code>nvim-lualine/lualine.nvim</code> 在 <code>VeryLazy</code> 时加载，直接使用 <code>catppuccin-mocha</code> 主题，使状态栏与整体配色完美融合，无需额外调校。</p>
</li>
<li>
<p><strong>图标与配对：mini.nvim 生态</strong><br />
选取了两个轻量的 mini 插件：<code>mini.icons</code> 为各类文件类型提供图标支持，按需延迟加载；<code>mini.pairs</code> 在进入插入模式时自动配对括号、引号等，保持编辑体验流畅。</p>
</li>
<li>
<p><strong>Git 装饰：gitsigns.nvim</strong><br />
<code>lewis6991/gitsigns.nvim</code> 在打开文件时即生效，用自定义符号（如 <code>┃</code> 表示新增或修改，<code>_</code> 表示删除）在左侧符号列标示变更状态，且区分暂存与未暂存的符号。配置还启用了当前行 blame 预览（行尾显示作者和时间），延迟 1 秒出现，同时限制超长文件禁用，兼顾性能与信息展示。</p>
</li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>return</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>3</div></div><div><span>        </span><span>"catppuccin/nvim"</span><span>,</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>name </span><span>=</span><span> </span><span>"catppuccin"</span><span>,</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>priority </span><span>=</span><span> </span><span>1000</span><span>,</span></div></div><div><div><div>6</div></div><div><span>        </span><span>config</span><span> </span><span>=</span><span> </span><span>function</span><span>()</span></div></div><div><div><div>7</div></div><div><span>            </span><span>require</span><span>(</span><span>"catppuccin"</span><span>).</span><span>setup</span><span>({</span></div></div><div><div><div>8</div></div><div><span><span>                </span></span><span>flavour </span><span>=</span><span> </span><span>"mocha"</span><span>,</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>})</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>vim.cmd.</span><span>colorscheme</span><span> </span><span>"catppuccin"</span></div></div><div><div><div>11</div></div><div><span>        </span><span>end</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>14</div></div><div><span>        </span><span>"folke/snacks.nvim"</span><span>,</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>priority </span><span>=</span><span> </span><span>1000</span><span>,</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>lazy </span><span>=</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>17</div></div><div><span>        </span><span>---</span><span>@type</span><span> </span><span>snacks.Config</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>opts </span><span>=</span><span> {</span></div></div><div><div><div>19</div></div><div><span><span>            </span></span><span>bigfile </span><span>=</span><span> { enabled </span><span>=</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>20</div></div><div><span><span>            </span></span><span>dashboard </span><span>=</span><span> { enabled </span><span>=</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>21</div></div><div><span><span>            </span></span><span>explorer </span><span>=</span><span> { enabled </span><span>=</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>22</div></div><div><span><span>            </span></span><span>image </span><span>=</span><span> { enabled </span><span>=</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>23</div></div><div><span><span>            </span></span><span>indent </span><span>=</span><span> { enabled </span><span>=</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>24</div></div><div><span><span>            </span></span><span>input </span><span>=</span><span> { enabled </span><span>=</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>25</div></div><div><span><span>            </span></span><span>notifier </span><span>=</span><span> {</span></div></div><div><div><div>26</div></div><div><span><span>                </span></span><span>enabled </span><span>=</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>27</div></div><div><span><span>                </span></span><span>timeout </span><span>=</span><span> </span><span>3000</span><span>,</span></div></div><div><div><div>28</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>29</div></div><div><span><span>            </span></span><span>picker </span><span>=</span><span> { enabled </span><span>=</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>30</div></div><div><span><span>            </span></span><span>quickfile </span><span>=</span><span> { enabled </span><span>=</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>31</div></div><div><span><span>            </span></span><span>scope </span><span>=</span><span> { enabled </span><span>=</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>32</div></div><div><span><span>            </span></span><span>scroll </span><span>=</span><span> { enabled </span><span>=</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>33</div></div><div><span><span>            </span></span><span>statuscolumn </span><span>=</span><span> { enabled </span><span>=</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>34</div></div><div><span><span>            </span></span><span>words </span><span>=</span><span> { enabled </span><span>=</span><span> </span><span>true</span><span> },</span></div></div><div><div><div>35</div></div><div><span><span>            </span></span><span>styles </span><span>=</span><span> {</span></div></div><div><div><div>36</div></div><div><span><span>                </span></span><span>notification </span><span>=</span><span> {</span></div></div><div><div><div>37</div></div><div><span>                    </span><span>-- wo = { wrap = true } -- Wrap notifications</span></div></div><div><div><div>38</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>39</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>40</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>41</div></div><div><span><span>        </span></span><span>keys </span><span>=</span><span> {</span></div></div><div><div><div>42</div></div><div><span>            </span><span>-- Top Pickers &amp; Explorer</span></div></div><div><div><div>43</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;&lt;space&gt;"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>smart</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"智能查找文件" </span><span>},</span></div></div><div><div><div>44</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;,"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>buffers</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"缓冲区" </span><span>},</span></div></div><div><div><div>45</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;/"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>grep</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"全文搜索" </span><span>},</span></div></div><div><div><div>46</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;:"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>command_history</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"命令历史" </span><span>},</span></div></div><div><div><div>47</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;n"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>notifications</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"通知历史" </span><span>},</span></div></div><div><div><div>48</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;e"</span><span>, </span><span>function</span><span>()</span><span> Snacks.</span><span>explorer</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"文件浏览器" </span><span>},</span></div></div><div><div><div>49</div></div><div><span>            </span><span>-- find</span></div></div><div><div><div>50</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;fb"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>buffers</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"缓冲区" </span><span>},</span></div></div><div><div><div>51</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;fc"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>files</span><span>({ cwd </span><span>=</span><span> vim.fn.</span><span>stdpath</span><span>(</span><span>"config"</span><span>) }) </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"查找配置文件" </span><span>},</span></div></div><div><div><div>52</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;ff"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>files</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"查找文件" </span><span>},</span></div></div><div><div><div>53</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;fg"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>git_files</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"查找 Git 文件" </span><span>},</span></div></div><div><div><div>54</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;fp"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>projects</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"项目" </span><span>},</span></div></div><div><div><div>55</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;fr"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>recent</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"最近" </span><span>},</span></div></div><div><div><div>56</div></div><div><span>            </span><span>-- git</span></div></div><div><div><div>57</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;gb"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>git_branches</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"Git Branches" </span><span>},</span></div></div><div><div><div>58</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;gl"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>git_log</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"Git Log" </span><span>},</span></div></div><div><div><div>59</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;gL"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>git_log_line</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"Git Log Line" </span><span>},</span></div></div><div><div><div>60</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;gs"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>git_status</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"Git Status" </span><span>},</span></div></div><div><div><div>61</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;gS"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>git_stash</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"Git Stash" </span><span>},</span></div></div><div><div><div>62</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;gd"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>git_diff</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"Git Diff (Hunks)" </span><span>},</span></div></div><div><div><div>63</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;gf"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>git_log_file</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"Git Log File" </span><span>},</span></div></div><div><div><div>64</div></div><div><span>            </span><span>-- gh</span></div></div><div><div><div>65</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;gi"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>gh_issue</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"GitHub Issues (open)" </span><span>},</span></div></div><div><div><div>66</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;gI"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>gh_issue</span><span>({ state </span><span>=</span><span> </span><span>"all" </span><span>}) </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"GitHub Issues</span></div></div><div><div><div>67</div></div><div><span>(all)</span><span>" },</span></div></div><div><div><div>68</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;gp"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>gh_pr</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"GitHub Pull Requests (open)" </span><span>},</span></div></div><div><div><div>69</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;gP"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>gh_pr</span><span>({ state </span><span>=</span><span> </span><span>"all" </span><span>}) </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"GitHub Pull Requests (all)" </span><span>},</span></div></div><div><div><div>70</div></div><div><span>            </span><span>-- Grep</span></div></div><div><div><div>71</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sb"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>lines</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"当前缓冲区行" </span><span>},</span></div></div><div><div><div>72</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sB"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>grep_buffers</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"搜索已打开缓冲区" </span><span>},</span></div></div><div><div><div>73</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sg"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>grep</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"搜索文本" </span><span>},</span></div></div><div><div><div>74</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sw"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>grep_word</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"选区或单词"</span><span>, mode </span><span>=</span><span> { </span><span>"n"</span><span>, </span><span>"x" </span><span>} },</span></div></div><div><div><div>75</div></div><div><span>            </span><span>-- search</span></div></div><div><div><div>76</div></div><div><span><span>            </span></span><span>{ </span><span>'&lt;leader&gt;s"'</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>registers</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"Registers" </span><span>},</span></div></div><div><div><div>77</div></div><div><span><span>            </span></span><span>{ </span><span>'&lt;leader&gt;s/'</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>search_history</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"搜索历史" </span><span>},</span></div></div><div><div><div>78</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sa"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>autocmds</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"自动命令" </span><span>},</span></div></div><div><div><div>79</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sb"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>lines</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"当前缓冲区行" </span><span>},</span></div></div><div><div><div>80</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sc"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>command_history</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"命令历史" </span><span>},</span></div></div><div><div><div>81</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sC"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>commands</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"命令" </span><span>},</span></div></div><div><div><div>82</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sd"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>diagnostics</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"诊断" </span><span>},</span></div></div><div><div><div>83</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sD"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>diagnostics_buffer</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"缓冲区诊断" </span><span>},</span></div></div><div><div><div>84</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sh"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>help</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"帮助页面" </span><span>},</span></div></div><div><div><div>85</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sH"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>highlights</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"高亮组" </span><span>},</span></div></div><div><div><div>86</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;si"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>icons</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"图标" </span><span>},</span></div></div><div><div><div>87</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sj"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>jumps</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"跳转列表" </span><span>},</span></div></div><div><div><div>88</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sk"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>keymaps</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"按键映射" </span><span>},</span></div></div><div><div><div>89</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sl"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>loclist</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"位置列表" </span><span>},</span></div></div><div><div><div>90</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sm"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>marks</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"标记" </span><span>},</span></div></div><div><div><div>91</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sM"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>man</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"手册" </span><span>},</span></div></div><div><div><div>92</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sp"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>lazy</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"搜索插件配置" </span><span>},</span></div></div><div><div><div>93</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sq"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>qflist</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"快速修复列表" </span><span>},</span></div></div><div><div><div>94</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sR"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>resume</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"恢复" </span><span>},</span></div></div><div><div><div>95</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;su"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>undo</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"撤销历史" </span><span>},</span></div></div><div><div><div>96</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;uC"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>colorschemes</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"配色方案" </span><span>},</span></div></div><div><div><div>97</div></div><div><span>            </span><span>-- LSP</span></div></div><div><div><div>98</div></div><div><span><span>            </span></span><span>{ </span><span>"gd"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>lsp_definitions</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"转到定义" </span><span>},</span></div></div><div><div><div>99</div></div><div><span><span>            </span></span><span>{ </span><span>"gD"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>lsp_declarations</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"转到声明" </span><span>},</span></div></div><div><div><div>100</div></div><div><span><span>            </span></span><span>{ </span><span>"gr"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>lsp_references</span><span>() </span><span>end</span><span>, nowait </span><span>=</span><span> </span><span>true</span><span>, desc </span><span>=</span><span> </span><span>"引用" </span><span>},</span></div></div><div><div><div>101</div></div><div><span><span>            </span></span><span>{ </span><span>"gI"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>lsp_implementations</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"转到实现" </span><span>},</span></div></div><div><div><div>102</div></div><div><span><span>            </span></span><span>{ </span><span>"gy"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>lsp_type_definitions</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"转到类型定义" </span><span>},</span></div></div><div><div><div>103</div></div><div><span><span>            </span></span><span>{ </span><span>"gai"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>lsp_incoming_calls</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"传入调用" </span><span>},</span></div></div><div><div><div>104</div></div><div><span><span>            </span></span><span>{ </span><span>"gao"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>lsp_outgoing_calls</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"传出调用" </span><span>},</span></div></div><div><div><div>105</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;ss"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>lsp_symbols</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"LSP 符号" </span><span>},</span></div></div><div><div><div>106</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;sS"</span><span>, </span><span>function</span><span>()</span><span> Snacks.picker.</span><span>lsp_workspace_symbols</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"LSP Workspace 符号" </span><span>},</span></div></div><div><div><div>107</div></div><div><span>            </span><span>-- Other</span></div></div><div><div><div>108</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;z"</span><span>, </span><span>function</span><span>()</span><span> Snacks.</span><span>zen</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"切换 Zen Mode" </span><span>},</span></div></div><div><div><div>109</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;Z"</span><span>, </span><span>function</span><span>()</span><span> Snacks.zen.</span><span>zoom</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"切换放大模式" </span><span>},</span></div></div><div><div><div>110</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;."</span><span>, </span><span>function</span><span>()</span><span> Snacks.</span><span>scratch</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"切换临时缓冲区" </span><span>},</span></div></div><div><div><div>111</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;S"</span><span>, </span><span>function</span><span>()</span><span> Snacks.scratch.</span><span>select</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"选择临时缓冲区" </span><span>},</span></div></div><div><div><div>112</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;n"</span><span>, </span><span>function</span><span>()</span><span> Snacks.notifier.</span><span>show_history</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"通知历史" </span><span>},</span></div></div><div><div><div>113</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;bd"</span><span>, </span><span>function</span><span>()</span><span> Snacks.</span><span>bufdelete</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"删除缓冲区" </span><span>},</span></div></div><div><div><div>114</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;cR"</span><span>, </span><span>function</span><span>()</span><span> Snacks.rename.</span><span>rename_file</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"重命名文件" </span><span>},</span></div></div><div><div><div>115</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;gB"</span><span>, </span><span>function</span><span>()</span><span> Snacks.</span><span>gitbrowse</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"Git 浏览"</span><span>, mode </span><span>=</span><span> { </span><span>"n"</span><span>, </span><span>"v" </span><span>} },</span></div></div><div><div><div>116</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;gg"</span><span>, </span><span>function</span><span>()</span><span> Snacks.</span><span>lazygit</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"Lazygit" </span><span>},</span></div></div><div><div><div>117</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;leader&gt;un"</span><span>, </span><span>function</span><span>()</span><span> Snacks.notifier.</span><span>hide</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"清除所有通知" </span><span>},</span></div></div><div><div><div>118</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;c-/&gt;"</span><span>, </span><span>function</span><span>()</span><span> Snacks.</span><span>terminal</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"切换终端" </span><span>},</span></div></div><div><div><div>119</div></div><div><span><span>            </span></span><span>{ </span><span>"&lt;c-_&gt;"</span><span>, </span><span>function</span><span>()</span><span> Snacks.</span><span>terminal</span><span>() </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"which_key_ignore" </span><span>},</span></div></div><div><div><div>120</div></div><div><span><span>            </span></span><span>{ </span><span>"]]"</span><span>, </span><span>function</span><span>()</span><span> Snacks.words.</span><span>jump</span><span>(vim.v.count1) </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"下一个引用"</span><span>, mode </span><span>=</span><span> { </span><span>"n"</span><span>, </span><span>"t" </span><span>} },</span></div></div><div><div><div>121</div></div><div><span><span>            </span></span><span>{ </span><span>"[["</span><span>, </span><span>function</span><span>()</span><span> Snacks.words.</span><span>jump</span><span>(</span><span>-</span><span>vim.v.count1) </span><span>end</span><span>, desc </span><span>=</span><span> </span><span>"上一个引用"</span><span>, mode </span><span>=</span><span> { </span><span>"n"</span><span>, </span><span>"t"</span></div></div><div><div><div>122</div></div><div><span>} },</span></div></div><div><div><div>123</div></div><div><span><span>            </span></span><span>{</span></div></div><div><div><div>124</div></div><div><span>                </span><span>"&lt;leader&gt;N"</span><span>,</span></div></div><div><div><div>125</div></div><div><span><span>                </span></span><span>desc </span><span>=</span><span> </span><span>"Neovim News"</span><span>,</span></div></div><div><div><div>126</div></div><div><span>                </span><span>function</span><span>()</span></div></div><div><div><div>127</div></div><div><span><span>                    </span></span><span>Snacks.</span><span>win</span><span>({</span></div></div><div><div><div>128</div></div><div><span><span>                        </span></span><span>file </span><span>=</span><span> vim.api.</span><span>nvim_get_runtime_file</span><span>(</span><span>"doc/news.txt"</span><span>, </span><span>false</span><span>)[</span><span>1</span><span>],</span></div></div><div><div><div>129</div></div><div><span><span>                        </span></span><span>width </span><span>=</span><span> </span><span>0.6</span><span>,</span></div></div><div><div><div>130</div></div><div><span><span>                        </span></span><span>height </span><span>=</span><span> </span><span>0.6</span><span>,</span></div></div><div><div><div>131</div></div><div><span><span>                        </span></span><span>wo </span><span>=</span><span> {</span></div></div><div><div><div>132</div></div><div><span><span>                            </span></span><span>spell </span><span>=</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>133</div></div><div><span><span>                            </span></span><span>wrap </span><span>=</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>134</div></div><div><span><span>                            </span></span><span>signcolumn </span><span>=</span><span> </span><span>"yes"</span><span>,</span></div></div><div><div><div>135</div></div><div><span><span>                            </span></span><span>statuscolumn </span><span>=</span><span> </span><span>" "</span><span>,</span></div></div><div><div><div>136</div></div><div><span><span>                            </span></span><span>conceallevel </span><span>=</span><span> </span><span>3</span><span>,</span></div></div><div><div><div>137</div></div><div><span><span>                        </span></span><span>},</span></div></div><div><div><div>138</div></div><div><span><span>                    </span></span><span>})</span></div></div><div><div><div>139</div></div><div><span>                </span><span>end</span><span>,</span></div></div><div><div><div>140</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>141</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>142</div></div><div><span>        </span><span>init</span><span> </span><span>=</span><span> </span><span>function</span><span>()</span></div></div><div><div><div>143</div></div><div><span><span>            </span></span><span>vim.api.</span><span>nvim_create_autocmd</span><span>(</span><span>"User"</span><span>, {</span></div></div><div><div><div>144</div></div><div><span><span>                </span></span><span>pattern </span><span>=</span><span> </span><span>"VeryLazy"</span><span>,</span></div></div><div><div><div>145</div></div><div><span>                </span><span>callback</span><span> </span><span>=</span><span> </span><span>function</span><span>()</span></div></div><div><div><div>146</div></div><div><span>                    </span><span>-- Setup some globals for debugging (lazy-loaded)</span></div></div><div><div><div>147</div></div><div><span>                    </span><span>_G</span><span>.</span><span>dd</span><span> </span><span>=</span><span> </span><span>function</span><span>(</span><span>...</span><span>)</span></div></div><div><div><div>148</div></div><div><span><span>                        </span></span><span>Snacks.debug.</span><span>inspect</span><span>(</span><span>...</span><span>)</span></div></div><div><div><div>149</div></div><div><span>                    </span><span>end</span></div></div><div><div><div>150</div></div><div><span>                    </span><span>_G</span><span>.</span><span>bt</span><span> </span><span>=</span><span> </span><span>function</span><span>()</span></div></div><div><div><div>151</div></div><div><span><span>                        </span></span><span>Snacks.debug.</span><span>backtrace</span><span>()</span></div></div><div><div><div>152</div></div><div><span>                    </span><span>end</span></div></div><div><div><div>153</div></div><div>
</div></div><div><div><div>154</div></div><div><span>                    </span><span>-- Override print to use snacks for `:=` command</span></div></div><div><div><div>155</div></div><div><span>                    </span><span>if</span><span> vim.fn.</span><span>has</span><span>(</span><span>"nvim-0.11"</span><span>) </span><span>==</span><span> </span><span>1</span><span> </span><span>then</span></div></div><div><div><div>156</div></div><div><span><span>                        </span></span><span>vim.</span><span>_print</span><span> </span><span>=</span><span> </span><span>function</span><span>(</span><span>_</span><span>,</span><span> ...</span><span>)</span></div></div><div><div><div>157</div></div><div><span>                            </span><span>dd</span><span>(</span><span>...</span><span>)</span></div></div><div><div><div>158</div></div><div><span>                        </span><span>end</span></div></div><div><div><div>159</div></div><div><span>                    </span><span>else</span></div></div><div><div><div>160</div></div><div><span><span>                        </span></span><span>vim.print </span><span>=</span><span> </span><span>_G</span><span>.dd</span></div></div><div><div><div>161</div></div><div><span>                    </span><span>end</span></div></div><div><div><div>162</div></div><div>
</div></div><div><div><div>163</div></div><div><span>                    </span><span>-- Create some toggle mappings</span></div></div><div><div><div>164</div></div><div><span><span>                    </span></span><span>Snacks.toggle.</span><span>option</span><span>(</span><span>"spell"</span><span>, { name </span><span>=</span><span> </span><span>"Spelling" </span><span>}):</span><span>map</span><span>(</span><span>"&lt;leader&gt;us"</span><span>)</span></div></div><div><div><div>165</div></div><div><span><span>                    </span></span><span>Snacks.toggle.</span><span>option</span><span>(</span><span>"wrap"</span><span>, { name </span><span>=</span><span> </span><span>"Wrap" </span><span>}):</span><span>map</span><span>(</span><span>"&lt;leader&gt;uw"</span><span>)</span></div></div><div><div><div>166</div></div><div><span><span>                    </span></span><span>Snacks.toggle.</span><span>option</span><span>(</span><span>"relativenumber"</span><span>, { name </span><span>=</span><span> </span><span>"Relative Number" </span><span>}):</span><span>map</span><span>(</span><span>"&lt;leader&gt;uL"</span><span>)</span></div></div><div><div><div>167</div></div><div><span><span>                    </span></span><span>Snacks.toggle.</span><span>diagnostics</span><span>():</span><span>map</span><span>(</span><span>"&lt;leader&gt;ud"</span><span>)</span></div></div><div><div><div>168</div></div><div><span><span>                    </span></span><span>Snacks.toggle.</span><span>line_number</span><span>():</span><span>map</span><span>(</span><span>"&lt;leader&gt;ul"</span><span>)</span></div></div><div><div><div>169</div></div><div><span><span>                    </span></span><span>Snacks.toggle.</span><span>option</span><span>(</span><span>"conceallevel"</span><span>,</span></div></div><div><div><div>170</div></div><div><span><span>                        </span></span><span>{ off </span><span>=</span><span> </span><span>0</span><span>, on </span><span>=</span><span> vim.o.conceallevel </span><span>&gt;</span><span> </span><span>0</span><span> </span><span>and</span><span> vim.o.conceallevel </span><span>or</span><span> </span><span>2</span><span> }):</span><span>map</span><span>(</span><span>"&lt;leader&gt;uc"</span><span>)</span></div></div><div><div><div>171</div></div><div><span><span>                    </span></span><span>Snacks.toggle.</span><span>treesitter</span><span>():</span><span>map</span><span>(</span><span>"&lt;leader&gt;uT"</span><span>)</span></div></div><div><div><div>172</div></div><div><span><span>                    </span></span><span>Snacks.toggle.</span><span>option</span><span>(</span><span>"background"</span><span>, { off </span><span>=</span><span> </span><span>"light"</span><span>, on </span><span>=</span><span> </span><span>"dark"</span><span>, name </span><span>=</span><span> </span><span>"Dark Background" </span><span>}):</span><span>map</span><span>(</span></div></div><div><div><div>173</div></div><div><span>                        </span><span>"&lt;leader&gt;ub"</span><span>)</span></div></div><div><div><div>174</div></div><div><span><span>                    </span></span><span>Snacks.toggle.</span><span>inlay_hints</span><span>():</span><span>map</span><span>(</span><span>"&lt;leader&gt;uh"</span><span>)</span></div></div><div><div><div>175</div></div><div><span><span>                    </span></span><span>Snacks.toggle.</span><span>indent</span><span>():</span><span>map</span><span>(</span><span>"&lt;leader&gt;ug"</span><span>)</span></div></div><div><div><div>176</div></div><div><span><span>                    </span></span><span>Snacks.toggle.</span><span>dim</span><span>():</span><span>map</span><span>(</span><span>"&lt;leader&gt;uD"</span><span>)</span></div></div><div><div><div>177</div></div><div><span>                </span><span>end</span><span>,</span></div></div><div><div><div>178</div></div><div><span><span>            </span></span><span>})</span></div></div><div><div><div>179</div></div><div><span>        </span><span>end</span><span>,</span></div></div><div><div><div>180</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>181</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>182</div></div><div><span>        </span><span>"folke/which-key.nvim"</span><span>,</span></div></div><div><div><div>183</div></div><div><span><span>        </span></span><span>event </span><span>=</span><span> </span><span>"VeryLazy"</span><span>,</span></div></div><div><div><div>184</div></div><div><span><span>        </span></span><span>opts </span><span>=</span><span> {</span></div></div><div><div><div>185</div></div><div><span><span>            </span></span><span>preset </span><span>=</span><span> </span><span>"helix"</span><span>,</span></div></div><div><div><div>186</div></div><div><span><span>            </span></span><span>spec </span><span>=</span><span> {},</span></div></div><div><div><div>187</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>188</div></div><div><span><span>        </span></span><span>keys </span><span>=</span><span> {</span></div></div><div><div><div>189</div></div><div><span><span>            </span></span><span>{</span></div></div><div><div><div>190</div></div><div><span>                </span><span>"&lt;leader&gt;?"</span><span>,</span></div></div><div><div><div>191</div></div><div><span>                </span><span>function</span><span>()</span></div></div><div><div><div>192</div></div><div><span>                    </span><span>require</span><span>(</span><span>"which-key"</span><span>).</span><span>show</span><span>({ global </span><span>=</span><span> </span><span>false</span><span> })</span></div></div><div><div><div>193</div></div><div><span>                </span><span>end</span><span>,</span></div></div><div><div><div>194</div></div><div><span><span>                </span></span><span>desc </span><span>=</span><span> </span><span>"当前缓冲区按键提示 (which-key)"</span><span>,</span></div></div><div><div><div>195</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>196</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>197</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>198</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>199</div></div><div><span>        </span><span>'nvim-lualine/lualine.nvim'</span><span>,</span></div></div><div><div><div>200</div></div><div><span><span>        </span></span><span>event </span><span>=</span><span> </span><span>"VeryLazy"</span><span>,</span></div></div><div><div><div>201</div></div><div><span><span>        </span></span><span>dependencies </span><span>=</span><span> { </span><span>'nvim-tree/nvim-web-devicons' </span><span>},</span></div></div><div><div><div>202</div></div><div><span><span>        </span></span><span>opts </span><span>=</span><span> {</span></div></div><div><div><div>203</div></div><div><span><span>            </span></span><span>theme </span><span>=</span><span> </span><span>'catppuccin-mocha'</span><span>,</span></div></div><div><div><div>204</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>205</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>206</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>207</div></div><div><span>        </span><span>'nvim-mini/mini.icons'</span><span>,</span></div></div><div><div><div>208</div></div><div><span><span>        </span></span><span>lazy </span><span>=</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>209</div></div><div><span><span>        </span></span><span>version </span><span>=</span><span> </span><span>false</span></div></div><div><div><div>210</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>211</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>212</div></div><div><span>        </span><span>'nvim-mini/mini.pairs'</span><span>,</span></div></div><div><div><div>213</div></div><div><span><span>        </span></span><span>event </span><span>=</span><span> </span><span>"InsertEnter"</span><span>,</span></div></div><div><div><div>214</div></div><div><span><span>        </span></span><span>opts </span><span>=</span><span> {},</span></div></div><div><div><div>215</div></div><div><span><span>        </span></span><span>version </span><span>=</span><span> </span><span>false</span></div></div><div><div><div>216</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>217</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>218</div></div><div><span>        </span><span>"lewis6991/gitsigns.nvim"</span><span>,</span></div></div><div><div><div>219</div></div><div><span><span>        </span></span><span>event </span><span>=</span><span> { </span><span>"BufReadPre"</span><span>, </span><span>"BufNewFile" </span><span>},</span></div></div><div><div><div>220</div></div><div><span><span>        </span></span><span>opts </span><span>=</span><span> {</span></div></div><div><div><div>221</div></div><div><span><span>            </span></span><span>signs                        </span><span>=</span><span> {</span></div></div><div><div><div>222</div></div><div><span><span>                </span></span><span>add          </span><span>=</span><span> { text </span><span>=</span><span> </span><span>'┃' </span><span>},</span></div></div><div><div><div>223</div></div><div><span><span>                </span></span><span>change       </span><span>=</span><span> { text </span><span>=</span><span> </span><span>'┃' </span><span>},</span></div></div><div><div><div>224</div></div><div><span><span>                </span></span><span>delete       </span><span>=</span><span> { text </span><span>=</span><span> </span><span>'_' </span><span>},</span></div></div><div><div><div>225</div></div><div><span><span>                </span></span><span>topdelete    </span><span>=</span><span> { text </span><span>=</span><span> </span><span>'‾' </span><span>},</span></div></div><div><div><div>226</div></div><div><span><span>                </span></span><span>changedelete </span><span>=</span><span> { text </span><span>=</span><span> </span><span>'~' </span><span>},</span></div></div><div><div><div>227</div></div><div><span><span>                </span></span><span>untracked    </span><span>=</span><span> { text </span><span>=</span><span> </span><span>'┆' </span><span>},</span></div></div><div><div><div>228</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>229</div></div><div><span><span>            </span></span><span>signs_staged                 </span><span>=</span><span> {</span></div></div><div><div><div>230</div></div><div><span><span>                </span></span><span>add          </span><span>=</span><span> { text </span><span>=</span><span> </span><span>'┃' </span><span>},</span></div></div><div><div><div>231</div></div><div><span><span>                </span></span><span>change       </span><span>=</span><span> { text </span><span>=</span><span> </span><span>'┃' </span><span>},</span></div></div><div><div><div>232</div></div><div><span><span>                </span></span><span>delete       </span><span>=</span><span> { text </span><span>=</span><span> </span><span>'_' </span><span>},</span></div></div><div><div><div>233</div></div><div><span><span>                </span></span><span>topdelete    </span><span>=</span><span> { text </span><span>=</span><span> </span><span>'‾' </span><span>},</span></div></div><div><div><div>234</div></div><div><span><span>                </span></span><span>changedelete </span><span>=</span><span> { text </span><span>=</span><span> </span><span>'~' </span><span>},</span></div></div><div><div><div>235</div></div><div><span><span>                </span></span><span>untracked    </span><span>=</span><span> { text </span><span>=</span><span> </span><span>'┆' </span><span>},</span></div></div><div><div><div>236</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>237</div></div><div><span><span>            </span></span><span>signs_staged_enable          </span><span>=</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>238</div></div><div><span><span>            </span></span><span>signcolumn                   </span><span>=</span><span> </span><span>true</span><span>, </span><span>-- Toggle with `:Gitsigns toggle_signs`</span></div></div><div><div><div>239</div></div><div><span><span>            </span></span><span>numhl                        </span><span>=</span><span> </span><span>false</span><span>, </span><span>-- Toggle with `:Gitsigns toggle_numhl`</span></div></div><div><div><div>240</div></div><div><span><span>            </span></span><span>linehl                       </span><span>=</span><span> </span><span>false</span><span>, </span><span>-- Toggle with `:Gitsigns toggle_linehl`</span></div></div><div><div><div>241</div></div><div><span><span>            </span></span><span>word_diff                    </span><span>=</span><span> </span><span>false</span><span>, </span><span>-- Toggle with `:Gitsigns toggle_word_diff`</span></div></div><div><div><div>242</div></div><div><span><span>            </span></span><span>watch_gitdir                 </span><span>=</span><span> {</span></div></div><div><div><div>243</div></div><div><span><span>                </span></span><span>follow_files </span><span>=</span><span> </span><span>true</span></div></div><div><div><div>244</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>245</div></div><div><span><span>            </span></span><span>auto_attach                  </span><span>=</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>246</div></div><div><span><span>            </span></span><span>attach_to_untracked          </span><span>=</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>247</div></div><div><span><span>            </span></span><span>current_line_blame           </span><span>=</span><span> </span><span>false</span><span>, </span><span>-- Toggle with `:Gitsigns toggle_current_line_blame`</span></div></div><div><div><div>248</div></div><div><span><span>            </span></span><span>current_line_blame_opts      </span><span>=</span><span> {</span></div></div><div><div><div>249</div></div><div><span><span>                </span></span><span>virt_text </span><span>=</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>250</div></div><div><span><span>                </span></span><span>virt_text_pos </span><span>=</span><span> </span><span>'eol'</span><span>, </span><span>-- 'eol' | 'overlay' | 'right_align'</span></div></div><div><div><div>251</div></div><div><span><span>                </span></span><span>delay </span><span>=</span><span> </span><span>1000</span><span>,</span></div></div><div><div><div>252</div></div><div><span><span>                </span></span><span>ignore_whitespace </span><span>=</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>253</div></div><div><span><span>                </span></span><span>virt_text_priority </span><span>=</span><span> </span><span>100</span><span>,</span></div></div><div><div><div>254</div></div><div><span><span>                </span></span><span>use_focus </span><span>=</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>255</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>256</div></div><div><span><span>            </span></span><span>current_line_blame_formatter </span><span>=</span><span> </span><span>'&lt;author&gt;, &lt;author_time:%R&gt; - &lt;summary&gt;'</span><span>,</span></div></div><div><div><div>257</div></div><div><span><span>            </span></span><span>blame_formatter              </span><span>=</span><span> </span><span>nil</span><span>, </span><span>-- Use default</span></div></div><div><div><div>258</div></div><div><span><span>            </span></span><span>sign_priority                </span><span>=</span><span> </span><span>6</span><span>,</span></div></div><div><div><div>259</div></div><div><span><span>            </span></span><span>update_debounce              </span><span>=</span><span> </span><span>100</span><span>,</span></div></div><div><div><div>260</div></div><div><span><span>            </span></span><span>status_formatter             </span><span>=</span><span> </span><span>nil</span><span>, </span><span>-- Use default</span></div></div><div><div><div>261</div></div><div><span><span>            </span></span><span>max_file_length              </span><span>=</span><span> </span><span>40000</span><span>, </span><span>-- Disable if file is longer than this (in lines)</span></div></div><div><div><div>262</div></div><div><span><span>            </span></span><span>preview_config               </span><span>=</span><span> {</span></div></div><div><div><div>263</div></div><div><span>                </span><span>-- Options passed to nvim_open_win</span></div></div><div><div><div>264</div></div><div><span><span>                </span></span><span>style </span><span>=</span><span> </span><span>'minimal'</span><span>,</span></div></div><div><div><div>265</div></div><div><span><span>                </span></span><span>relative </span><span>=</span><span> </span><span>'cursor'</span><span>,</span></div></div><div><div><div>266</div></div><div><span><span>                </span></span><span>row </span><span>=</span><span> </span><span>0</span><span>,</span></div></div><div><div><div>267</div></div><div><span><span>                </span></span><span>col </span><span>=</span><span> </span><span>1</span></div></div><div><div><div>268</div></div><div><span><span>            </span></span><span>},</span></div></div><div><div><div>269</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>270</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>271</div></div><div>
</div></div><div><div><div>272</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>]]></content>
    </entry>
    <entry>
      <id>https://bill.is-a.dev/posts/tree-command-usage/</id>
      <title type="text">使用 tree 命令输出文件夹结构</title>
      <published>2026-05-17T00:00:00.000Z</published>
      <updated>2026-05-17T00:00:00.000Z</updated>
      <author><name>CMBill</name></author>
      <link rel="alternate" href="https://bill.is-a.dev/posts/tree-command-usage/"/>
      <summary type="text">tree 命令的安装、常用选项详解及实用场景示例，帮助快速输出文件夹结构。</summary>
      <content type="html"><![CDATA[<p>与其他常见发行版不同，在 Arch Linux 上 <code>tree</code> 命令通常不是系统自带的。</p>
<section><h2>1 安装与基本用法<a href="#1-安装与基本用法"><span>#</span></a></h2><ul>
<li><strong>安装</strong>：在终端执行 <code>sudo pacman -S tree</code>。</li>
<li><strong>基本语法</strong>：安装后，使用 <code>tree [选项] [目录]</code> 的格式即可输出目录树。</li>
<li><strong>查看当前目录结构</strong>：直接输入 <code>tree</code> 并回车，会以树形结构清晰地展示当前目录下的所有文件和文件夹。</li>
<li><strong>查看指定目录</strong>：你还可以指定路径，例如 <code>tree /etc</code> 来查看特定目录的结构。</li>
</ul></section>
<section><h2>2 常用选项详解<a href="#2-常用选项详解"><span>#</span></a></h2><p><code>tree</code> 命令的强大之处在于它丰富的参数选项，可以灵活地控制输出内容。</p>

<table><thead><tr><th>选项</th><th>功能说明</th><th>使用示例</th></tr></thead><tbody><tr><td><code>-a</code></td><td>显示所有文件，包括以点 <code>.</code> 开头的隐藏文件。</td><td><code>tree -a</code></td></tr><tr><td><code>-d</code></td><td>只显示目录，不显示文件，用于快速查看目录骨架。</td><td><code>tree -d</code></td></tr><tr><td><code>-L</code></td><td>限制目录的显示深度，比如 <code>-L 2</code> 只显示到第二级子目录，防止输出过多。</td><td><code>tree -L 2</code></td></tr><tr><td><code>-P</code></td><td>仅列出匹配指定模式的文件，支持通配符。</td><td><code>tree -P "*.py"</code></td></tr><tr><td><code>-I</code></td><td>排除匹配指定模式的文件或目录，多个模式用竖线 <code>|</code> 分隔。</td><td><code>tree -I "node_modules|.git"</code></td></tr><tr><td><code>-h</code></td><td>以人类可读的格式（如 KB、MB）显示文件大小。</td><td><code>tree -h</code></td></tr><tr><td><code>-p</code></td><td>显示每个文件的权限信息。</td><td><code>tree -p</code></td></tr><tr><td><code>-f</code></td><td>为每个文件或目录显示完整的路径前缀。</td><td><code>tree -f</code></td></tr><tr><td><code>-D</code></td><td>显示文件或目录的最后修改时间。</td><td><code>tree -D</code></td></tr><tr><td><code>-o</code></td><td>将输出结果导出到指定的文件中。</td><td><code>tree -o structure.txt</code></td></tr><tr><td><code>--du</code></td><td>显示每个目录的磁盘使用情况（以块为单位）。</td><td><code>tree --du</code></td></tr></tbody></table></section>
<section><h2>3 实用场景示例<a href="#3-实用场景示例"><span>#</span></a></h2><p>以下是一些针对特定场景的命令组合：</p><ul>
<li><strong>只显示项目目录结构，限制深度</strong>：<code>tree -d -L 2</code>，这会只显示目录，并且限制深度为 2 层。</li>
<li><strong>排除干扰目录，显示完整结构</strong>：<code>tree -I 'node_modules|.git|__pycache__'</code>，这样可以忽略开发中常见的依赖或缓存文件夹。</li>
<li><strong>查看指定目录，并显示文件大小</strong>：<code>tree -h /home/user/Documents</code>，这会在输出时以易读的格式显示文件大小。</li>
<li><strong>导出目录结构</strong>：<code>tree -L 3 -o my_project_structure.txt</code>，这会将限制深度为 3 层的目录树输出到文件中。</li>
</ul></section>
<section><h2>4 注意事项<a href="#4-注意事项"><span>#</span></a></h2><ul>
<li><strong>谨慎处理大型目录</strong>：在根目录（<code>/</code>）或包含海量子目录（如 <code>node_modules</code>）的目录中运行 <code>tree</code> 时，务必用 <code>-L</code> 或 <code>-I</code> 限制范围，否则可能会因输出量巨大而卡顿。</li>
<li><strong>处理中文乱码</strong>：<code>tree</code> 命令原生支持 UTF-8。如果出现乱码，通常是终端环境问题。可以先检查并设置 <code>locale</code> 环境变量（如 <code>export LANG=zh_CN.UTF-8</code>）来解决。</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://bill.is-a.dev/posts/zsh-configuration-optimization/</id>
      <title type="text">Zsh 配置与优化</title>
      <published>2026-05-15T00:00:00.000Z</published>
      <updated>2026-06-15T00:00:00.000Z</updated>
      <author><name>CMBill</name></author>
      <link rel="alternate" href="https://bill.is-a.dev/posts/zsh-configuration-optimization/"/>
      <summary type="text">Zsh 终端配置指南，包括安装、基本设置、插件管理器 Zinit 的使用，以及各种优化配置。</summary>
      <content type="html"><![CDATA[<section><h2>1 安装<a href="#1-安装"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>pacman</span><span> </span><span>-S</span><span> </span><span>zsh</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>2 更改默认终端<a href="#2-更改默认终端"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>chsh</span><span> </span><span>-l</span><span> </span><span># 查看安装了哪些 Shell</span></div></div><div><div><div>2</div></div><div><span>chsh</span><span> </span><span>-s</span><span> </span><span>/usr/bin/zsh</span><span> </span><span># 修改当前账户的默认 Shell</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>3 切换到 Zsh 并配置<a href="#3-切换到-zsh-并配置"><span>#</span></a></h2><p>第一次启动时按照提示配置即可，也可以直接编辑 <code>.zshrc</code> 文件。</p><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># Lines configured by zsh-newuser-install</span></div></div><div><div><div>2</div></div><div><span>HISTFILE</span><span>=</span><span>~/.histfile</span></div></div><div><div><div>3</div></div><div><span>HISTSIZE</span><span>=</span><span>99999</span></div></div><div><div><div>4</div></div><div><span>SAVEHIST</span><span>=</span><span>99999</span></div></div><div><div><div>5</div></div><div><span>setopt</span><span> </span><span>autocd</span><span> </span><span>beep</span><span> </span><span>extendedglob</span><span> </span><span>nomatch</span><span> </span><span>notify</span></div></div><div><div><div>6</div></div><div><span>bindkey</span><span> </span><span>-v</span></div></div><div><div><div>7</div></div><div><span># End of lines configured by zsh-newuser-install</span></div></div><div><div><div>8</div></div><div><span># The following lines were added by compinstall</span></div></div><div><div><div>9</div></div><div><span>zstyle</span><span> </span><span>:compinstall</span><span> </span><span>filename</span><span> </span><span>'/home/bill/.zshrc'</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>autoload</span><span> </span><span>-Uz</span><span> </span><span>compinit</span></div></div><div><div><div>12</div></div><div><span>compinit</span></div></div><div><div><div>13</div></div><div><span># End of lines added by compinstall</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>### Added by Zinit's installer</span></div></div><div><div><div>16</div></div><div><span>if</span><span> </span><span>[[</span><span> </span><span>!</span><span> </span><span>-f</span><span> $HOME/.local/share/zinit/zinit.git/zinit.zsh </span><span>]];</span><span> </span><span>then</span></div></div><div><div><div>17</div></div><div><span>    </span><span>print</span><span> </span><span>-P</span><span> </span><span>"%F{33} %F{220}Installing %F{33}ZDHARMA-CONTINUUM%F{220} Initiative Plugin Manager (%F{33}zdharma-continuum/zinit%F{220})…%f"</span></div></div><div><div><div>18</div></div><div><span>    </span><span>command</span><span> </span><span>mkdir</span><span> </span><span>-p</span><span> </span><span>"</span><span>$HOME</span><span>/.local/share/zinit"</span><span> </span><span>&amp;&amp;</span><span> </span><span>command</span><span> </span><span>chmod</span><span> </span><span>g-rwX</span><span> </span><span>"</span><span>$HOME</span><span>/.local/share/zinit"</span></div></div><div><div><div>19</div></div><div><span>    </span><span>command</span><span> </span><span>git</span><span> </span><span>clone</span><span> </span><span>https://github.com/zdharma-continuum/zinit</span><span> </span><span>"</span><span>$HOME</span><span>/.local/share/zinit/zinit.git"</span><span> </span><span>&amp;&amp;</span><span> </span><span>\</span></div></div><div><div><div>20</div></div><div><span>        </span><span>print</span><span> </span><span>-P</span><span> </span><span>"%F{33} %F{34}Installation successful.%f%b"</span><span> </span><span>||</span><span> </span><span>\</span></div></div><div><div><div>21</div></div><div><span>        </span><span>print</span><span> </span><span>-P</span><span> </span><span>"%F{160} The clone has failed.%f%b"</span></div></div><div><div><div>22</div></div><div><span>fi</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>source</span><span> </span><span>"</span><span>$HOME</span><span>/.local/share/zinit/zinit.git/zinit.zsh"</span></div></div><div><div><div>25</div></div><div><span>autoload</span><span> </span><span>-Uz</span><span> </span><span>_zinit</span></div></div><div><div><div>26</div></div><div><span>((</span><span> ${+_comps} </span><span>))</span><span> </span><span>&amp;&amp;</span><span> _comps</span><span>[</span><span>zinit</span><span>]</span><span>=</span><span>_zinit</span></div></div><div><div><div>27</div></div><div><span>### End of Zinit's installer chunk</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span># 绑定键盘上的特殊按键如End，可以命令行里按cat回车，再按下对应的按键即可查看</span></div></div><div><div><div>30</div></div><div><span>bindkey</span><span>  </span><span>"^[[H"</span><span>   </span><span>beginning-of-line</span></div></div><div><div><div>31</div></div><div><span>bindkey</span><span>  </span><span>"^[[F"</span><span>   </span><span>end-of-line</span></div></div><div><div><div>32</div></div><div><span>bindkey</span><span>  </span><span>"^[[3~"</span><span>  </span><span>delete-char</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span># 配置默认编辑器</span></div></div><div><div><div>35</div></div><div><span>export</span><span> EDITOR</span><span>=</span><span>'nvim'</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span># 配置插件</span></div></div><div><div><div>38</div></div><div><span>zinit</span><span> </span><span>light</span><span> </span><span>zsh-users/zsh-completions</span></div></div><div><div><div>39</div></div><div><span>zinit</span><span> </span><span>light</span><span> </span><span>zsh-users/zsh-autosuggestions</span></div></div><div><div><div>40</div></div><div><span>zinit</span><span> </span><span>light</span><span> </span><span>zdharma-continuum/fast-syntax-highlighting</span></div></div><div><div><div>41</div></div><div><span>zinit</span><span> </span><span>light</span><span> </span><span>agkozak/zsh-z</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span># Starship</span></div></div><div><div><div>44</div></div><div><span>eval</span><span> </span><span>"</span><span>$(</span><span>starship</span><span> init zsh</span><span>)</span><span>"</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span># 终端语言</span></div></div><div><div><div>47</div></div><div><span>export</span><span> LANG</span><span>=</span><span>zh_CN.UTF-8</span></div></div><div><div><div>48</div></div><div><span>export</span><span> LC_ALL</span><span>=</span><span>zh_CN.UTF-8</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span># 别名</span></div></div><div><div><div>51</div></div><div><span>alias</span><span> ls</span><span>=</span><span>'ls --color=auto'</span></div></div><div><div><div>52</div></div><div><span>alias</span><span> ll</span><span>=</span><span>'ls -alhF'</span></div></div><div><div><div>53</div></div><div><span>alias</span><span> grep</span><span>=</span><span>'grep --color=auto'</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><section><h3>3.1 基本 Zsh 设置<a href="#31-基本-zsh-设置"><span>#</span></a></h3><p>这一部分也是初始化的时候设置的。</p><ul>
<li>
<p><strong>历史记录</strong><br />
<code>HISTFILE=~/.histfile</code>：指定历史命令保存文件。
<code>HISTSIZE=99999</code> 和 <code>SAVEHIST=99999</code>：内存中及文件中的历史命令最大数量。</p>
</li>
<li>
<p><strong>Shell 选项</strong><br />
<code>setopt autocd beep extendedglob nomatch notify</code>：</p>
<ul>
<li><code>autocd</code>：直接输入目录名即可进入。</li>
<li><code>beep</code>：出错时响铃。</li>
<li><code>extendedglob</code>：启用扩展模式匹配。</li>
<li><code>nomatch</code>：无匹配时不报错。</li>
<li><code>notify</code>：后台任务完成时立即通知。</li>
</ul>
</li>
<li>
<p><strong>键绑定</strong><br />
<code>bindkey -v</code>：使用 <code>vi</code> 风格命令行编辑（默认是 <code>emacs</code> 风格）。</p>
</li>
</ul></section><section><h3>3.2 命令补全系统<a href="#32-命令补全系统"><span>#</span></a></h3><p>通过 <code>compinit</code> 加载 Zsh 自带的智能补全，并设置 <code>compinstall</code> 保存补全配置。</p></section><section><h3>3.3 插件管理器 Zinit<a href="#33-插件管理器-zinit"><span>#</span></a></h3><p>参考 <a href="https://github.com/zdharma-continuum/zinit" target="_blank">https://github.com/zdharma-continuum/zinit</a></p><ul>
<li><strong>自动安装</strong>：若未安装 Zinit，脚本会从 GitHub 克隆到 <code>~/.local/share/zinit</code>。</li>
<li><strong>加载 Zinit</strong>：然后加载其核心文件及补全定义。<br />
Zinit 用于管理 Zsh 插件，轻量且高效。</li>
</ul></section><section><h3>3.4 键盘按键修正<a href="#34-键盘按键修正"><span>#</span></a></h3><p>针对终端发送的转义序列，绑定常用按键：</p><ul>
<li><code>^[[H</code>：<code>Home</code> → 行首</li>
<li><code>^[[F</code>：<code>End</code> → 行尾</li>
<li><code>^[[3~</code>：<code>Delete</code> → 删除光标后字符</li>
</ul></section><section><h3>3.5 编辑器环境变量<a href="#35-编辑器环境变量"><span>#</span></a></h3><p><code>export EDITOR='nvim'</code>：设置 Neovim 为默认文本编辑器。</p></section><section><h3>3.6 加载的 Zinit 插件<a href="#36-加载的-zinit-插件"><span>#</span></a></h3><ul>
<li><code>zsh-users/zsh-completions</code>：提供额外的补全定义。</li>
<li><code>zsh-users/zsh-autosuggestions</code>：根据历史记录实时建议命令（按 <code>→</code> 补全）。</li>
<li><code>zdharma-continuum/fast-syntax-highlighting</code>：高性能语法高亮，类似于 <code>zsh-syntax-highlighting</code>。</li>
<li><code>agkozak/zsh-z</code>：智能目录跳转，根据访问频率快速切换到常用目录，类似于 <code>autojump</code>。 使用 <a href="https://github.com/ajeetdsouza/zoxide" target="_blank">ajeetdsouza/zoxide</a> 替代。</li>
</ul></section><section><h3>3.7 提示符 Starship<a href="#37-提示符-starship"><span>#</span></a></h3><p><code>eval "$(starship init zsh)"</code>：启用 <a href="https://github.com/starship/starship" target="_blank">Starship</a> 跨 shell 提示符，显示 Git 状态、时间、路径等丰富信息。</p></section><section><h3>3.8 终端语言环境<a href="#38-终端语言环境"><span>#</span></a></h3><p><code>export LANG=zh_CN.UTF-8</code> 和 <code>export LC_ALL=zh_CN.UTF-8</code>：设置用户终端为中文 UTF-8 编码。</p></section><section><h3>3.9 常用别名<a href="#39-常用别名"><span>#</span></a></h3><ul>
<li><code>ls='ls --color=auto'</code>：彩色显示目录内容。</li>
<li><code>ll='ls -alhF'</code>：列出所有文件（包括隐藏），人性化大小，并标记类型。</li>
<li><code>grep='grep --color=auto'</code>：匹配结果高亮显示。</li>
</ul></section></section>]]></content>
    </entry>
</feed>
