<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://maoguangming.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://maoguangming.com/" rel="alternate" type="text/html" /><updated>2026-03-18T22:48:38-07:00</updated><id>https://maoguangming.com/feed.xml</id><title type="html">Guangming Mao’s Blog</title><subtitle></subtitle><entry xml:lang="zh"><title type="html">迁移 com.apple.mediaanalysisd 到外置 SSD</title><link href="https://maoguangming.com/blog/migrate-mediaanalysisd-to-external-ssd.html" rel="alternate" type="text/html" title="迁移 com.apple.mediaanalysisd 到外置 SSD" /><published>2025-08-17T08:09:00-07:00</published><updated>2025-08-17T08:09:00-07:00</updated><id>https://maoguangming.com/blog/migrate-mediaanalysisd-to-external-ssd</id><content type="html" xml:base="https://maoguangming.com/blog/migrate-mediaanalysisd-to-external-ssd.html"><![CDATA[<h2 id="楔子">楔子</h2>

<p>我有台老款丐版 Mac Mini，硬盘只有 256GB，勉强凑合了好几年。日常只用来上下网，但时不时冒出的“磁盘空间不足”警告，着实让人头疼。每次空间告急，我都要去清一波数据，从文档到下载，上次甚至连 Xcode 和 Office 都删了，这电脑也称得上一句家徒四壁了。</p>

<p>昨天，我想下载个软件更新包，可试了好几次都下载失败。排除了网络问题后，我发现硬盘空间居然又满了！打开设置里的“储存空间”一看，好家伙，可用空间只剩不到 200MB，而“系统数据”竟然变本加厉占了整整 180GB！我实在是删无可删，无处可苟了。</p>

<h2 id="幕后黑手">幕后黑手</h2>

<p>“系统数据”在家目录下主要是 <code class="language-plaintext highlighter-rouge">~/Library</code>，一番排查后，我发现两个大头：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">~/Library/Caches</code>：缓存，随便删。</li>
  <li><code class="language-plaintext highlighter-rouge">~/Library/Containers</code>：macOS 应用的沙盒环境，这个不能直接删。</li>
</ul>

<p>其中，<code class="language-plaintext highlighter-rouge">~/Library/Containers</code> 下面的 <code class="language-plaintext highlighter-rouge">com.apple.mediaanalysisd</code> 更是触目惊心，独占 130G，顶得上我电脑硬盘的半壁江山了。</p>

<p>研究了一番，我了解到这个目录主要是 macOS 在后台进行人脸识别、物体分类和 Spotlight 索引等工作时产生的缓存。照片图库越大，它的体积就越恐怖。虽然可以临时删除，但是它会随时间慢慢重建回来，治标不治本。鉴于它的体积，我只有把它挪走，这台电脑才能继续苟下去。</p>

<h2 id="软链接">软链接</h2>

<p>一开始，我的想法很简单：把这个目录挪到外置 SSD 上，然后在原地做一个软链接。但现实很骨感，<code class="language-plaintext highlighter-rouge">~/Library/Containers</code> 属于 macOS 的沙盒保护区域。为了防止恶意应用通过软链接访问敏感文件，系统对其做了严格的权限控制。因此，这个方法行不通。</p>

<h2 id="挂载">挂载</h2>

<p>问了下 ChatGPT，它提供了一个挂载的方案：将外置 SSD 上的一个分区直接挂载到 com.apple.mediaanalysisd 目录的位置，让系统以为它还在内置硬盘里。</p>

<ol>
  <li>
    <p><strong>准备外置 SSD</strong>：用磁盘工具新建了一个 APFS 卷，命名为 <code class="language-plaintext highlighter-rouge">MediaAnalysisVol</code>。这是专门用来存储这个大缓存的。</p>
  </li>
  <li>
    <p><strong>迁移数据</strong>：（可选，因为之后会重建）为了确保数据完整，先在终端里用 <code class="language-plaintext highlighter-rouge">killall mediaanalysisd</code> 关掉相关进程，然后用 <code class="language-plaintext highlighter-rouge">rsync</code> 命令行把 <code class="language-plaintext highlighter-rouge">com.apple.mediaanalysisd</code> 里的所有数据完整地复制到新卷里。<code class="language-plaintext highlighter-rouge">rsync</code> 是个好东西，它可以完整地保留文件权限、扩展属性等信息。</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rsync <span class="nt">-aEHX</span> <span class="nt">--xattrs</span> <span class="nt">--protect-args</span> ~/Library/Containers/com.apple.mediaanalysisd/ /Volumes/MediaAnalysisVol/
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>清空原目录</strong>：将内置硬盘上的原目录内容删除，只保留一个空目录。</p>
  </li>
  <li>
    <p><strong>挂载新卷</strong>：先用 <code class="language-plaintext highlighter-rouge">diskutil list</code> 找到新卷的设备名（例如 /dev/disk7s2），然后把其挂载到目标路径。</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>diskutil enableOwnership /dev/disk7s2
<span class="nb">sudo </span>mount_apfs /dev/disk7s2 ~/Library/Containers/com.apple.mediaanalysisd
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>开机自动挂载</strong>：为了避免每次开机都手动挂载，我们可以配置 /etc/fstab 文件。先用 <code class="language-plaintext highlighter-rouge">diskutil info</code> 找到新卷的 UUID，然后把挂载命令加到 <code class="language-plaintext highlighter-rouge">fstab</code> 里。</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">UUID</span><span class="o">=</span>12345678-ABCD-1234-ABCD-1234567890AB /Users/ahahaha/Library/Containers/com.apple.mediaanalysisd apfs rw 0 0
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="后续">后续</h2>

<p>现状良好，又可以苟一段时间了！</p>]]></content><author><name>Guangming Mao</name></author><category term="blog" /><category term="macos" /><summary type="html"><![CDATA[楔子]]></summary></entry><entry xml:lang="zh"><title type="html">Gosund SW5智能开关接入Home Assistant</title><link href="https://maoguangming.com/blog/gosund-sw5-in-homeassistant.html" rel="alternate" type="text/html" title="Gosund SW5智能开关接入Home Assistant" /><published>2024-03-23T02:18:00-07:00</published><updated>2024-03-23T02:18:00-07:00</updated><id>https://maoguangming.com/blog/gosund-sw5-in-homeassistant</id><content type="html" xml:base="https://maoguangming.com/blog/gosund-sw5-in-homeassistant.html"><![CDATA[<blockquote>
  <p>安装开关涉及强电，请务必注意人身安全并遵守当地法律法规！！！</p>
</blockquote>

<h2 id="楔子">楔子</h2>

<p>去年搬家后，在新家里发现有三个前房主留下的智能开关，可以连WiFi的那种，样子平平，在墙上倒是不显突兀。拆下来发现是一个叫<a href="https://us.gosund.com/products/app-gosund-smart-light-switch-sw5">Gosund</a>的牌子，正好当时正准备把家里的开关换成智能开关，也看了不少其他牌子，最后兜兜转转，还是去Temu上买了一些一样的。Amazon上其实也有，不过不知为何，名字已经换成了<a href="https://a.co/d/hMzTv45">Ghome</a>。</p>

<p>关于智能家居，我的目的很简单，就是能躺在床上用手机开关灯。由于我在家里已经部署了<a href="https://www.home-assistant.io">Home Assistant</a>，这里只记录一下我是怎么把这些开关集成进来，并且导入Apple Home和Amazon Alexa里面的，关于Home Assistant的其他使用经验以后有机会可以继续分享。</p>

<h2 id="基本思路">基本思路</h2>

<ol>
  <li>把所有开关集成到Home Assistant里。</li>
  <li>选择一些实体（比如开关或灯）接入智能家居。
    <ol>
      <li>Apple Home：由于家里人基本都是用的iPhone，我有一台Apple TV当家居中枢，Home Assistant里的设备可以用<a href="https://www.home-assistant.io/integrations/homekit/">HomeKit Bridge集成</a>接入过来，以供家里的苹果设备控制。</li>
      <li>Alexa：家里还有一些Echo Dot，关于如何接入Alexa，具体可以参考<a href="https://www.home-assistant.io/integrations/alexa/">官方Amazon Alexa文档</a>。我选用的是<a href="https://www.home-assistant.io/integrations/emulated_hue/">Emulated Hue集成</a>，可以很方便的做到用Alexa语音控制开关灯。</li>
    </ol>
  </li>
</ol>

<p><img src="../images/switch-ha-smarthome.svg" alt="智能开关集成到Home Assistant" /></p>

<h2 id="集成到home-assistant">集成到Home Assistant</h2>

<p>说起类似的WiFi智能开关，不得不提一家叫<a href="https://www.tuya.com/cn/">涂鸦智能</a>的公司，我猜他提供了一整套智能家居解决方案，厂商只需要集成他们的芯片并使用背后的涂鸦云，就可以很方便的把自己的设备变「智能」，并接入Alexa或者Google Home之类的智能家居平台。这种开关一搜一大把，本文内容也部分适用。</p>

<p>我第一次发现这个还是几年前用Costco买的一些Feit的智能灯泡，一开始一直使用Feit的官方App，后来偶然从Reddit上看到用一个叫「Tuya Smart」的App也可以控制这些设备，试用后发现界面都是一模一样的。再之后才得知这家叫「涂鸦」的公司。</p>

<p>闲话休提，言归正传。</p>

<h3 id="home-assistant官方的tuya集成">Home Assistant官方的Tuya集成</h3>

<p>如果想把这些开关集成到Home Assistant里，最简单的方法是使用官方的<a href="https://www.home-assistant.io/integrations/tuya/">Tuya集成</a>。
<img src="../images/tuya-intergration.png" alt="tuya集成" /></p>

<p>这部分Home Assistant的文档已经十分详细，不再赘述。</p>

<p>但是这种方式依然依赖涂鸦的服务器，从按下按钮到开关接通把灯点亮中间经过了网络请求，能明显感受到延迟。万一涂鸦的服务器挂了，我也不知具体会有什么后果。猜测</p>

<h3 id="localtuya">localtuya</h3>

<p>秉持「本地优先」的原则，社区有很多优秀的集成可以跳过服务器直接和设备通信，我使用的是<a href="https://github.com/rospogrigio/localtuya">localtuya</a>。主要区别是配置要复杂一些，需要注册涂鸦的账号并且获取设备密钥与ID，这部分文档也十分详细，同样不再赘述。</p>

<h3 id="esphome">ESPHome</h3>

<p>如果想彻底不依赖涂鸦的服务器，可以给这些设备刷自定义固件。涂鸦设备里面的芯片前几年主要是<a href="https://en.wikipedia.org/wiki/ESP8266">ESP8266</a>，最近好多都换成了Beken的。ESP芯片可以刷<a href="https://esphome.io">ESPHome</a>或者<a href="https://tasmota.github.io/docs/">Tasmota</a>，Beken的芯片同样有<a href="https://github.com/libretiny-eu/libretiny?tab=readme-ov-file">LibreTiny</a>和<a href="https://github.com/openshwprojects/OpenBK7231T_App">OpenBeken</a>，ESPHome在版本2023.9.0后也支持了LibreTiny。这些芯片都有留出UART引脚支持烧录程序。</p>

<p>使用Tasmota或者OpenBeken接入Home Assistant需要自己配置相应的MQTT节点，而ESPHome可以直接集成到Home Assistant里面，我直接刷的ESPHome固件。</p>

<h4 id="准备工具">准备工具</h4>

<ol>
  <li>烧录过程中需要往串口读写数据，最简单的方法是准备一个USB转TTL转换器，很便宜。</li>
  <li>把转换器引脚连到PCB板引脚上，烙铁使得好的可以自己焊，我电焊技术不行，用的跳线，BDM支架和探针，我要刷十几个开关，这样也比较方便。</li>
</ol>

<p>设置好后大概是这个样子：</p>

<p><img src="../images/bdm-frame.png" alt="BDM frame" /></p>

<h4 id="接线">接线</h4>

<p>首先拆开开关，找到烧录口。我买到的这些Gosund开关的型号是SW5-A-V2.1，芯片是Beken的BK7231N。开关背后使用4根螺丝固定，很容易拆开，具体拆解可以参考Elektroda的<a href="https://www.elektroda.com/rtvforum/topic3892160.html">这个帖子</a>，除了芯片是BK7231T，其余部分都是一样的。</p>

<p><img src="../images/gosund-sw5-bk7231n.png" alt="SW5-A-V2.1 BK7231N" /></p>

<p>按照<a href="https://docs.libretiny.eu/docs/platform/beken-72xx/#flashing">LibreTiny文档</a>，一般而言，BK7231芯片接线是这样的：</p>

<table>
  <thead>
    <tr>
      <th>USB转TTL转换器引脚</th>
      <th>芯片UART引脚</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>RX</td>
      <td>TX1</td>
    </tr>
    <tr>
      <td>TX</td>
      <td>RX1</td>
    </tr>
    <tr>
      <td>Ground (GND)</td>
      <td>Ground (GND)</td>
    </tr>
  </tbody>
</table>

<p>然后是供电，这个开关有一个好处是提供了一个5V的供电串口，用这个不仅可以给芯片供电，继电器也可以用，刷完固件可以立马验证结果，同时这根供电线可以接一个开关，对后续烧录时重启比较方便。按照下图接好线：</p>

<p><img src="../images/gosund-sw5-wiring.png" alt="Gosund SW5 Wiring" /></p>

<h4 id="烧录过程">烧录过程</h4>

<p>烧录工具我选择的是LibreTiny推荐的<a href="https://docs.libretiny.eu/docs/flashing/tools/ltchiptool/">ltchiptool</a>。ltchiptool是python写的，自带一个wxPython写的GUI程序，看文档感觉在Windows/Linux上应该很稳定，我在macOS上会经常崩溃，所以用的是命令行。</p>

<p>烧录过程：</p>

<ol>
  <li>首先断电，从命令行执行烧录程序</li>
  <li>立马打开供电，芯片会进入烧录模式，我有时候要多试几次才能成功开始烧录</li>
</ol>

<p>首先备份原厂固件，万一有用呢：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ltchiptool <span class="nt">-v</span> flash <span class="nb">read</span> <span class="nt">-d</span> /dev/cu.usbserial-B0010WQ0 <span class="nt">-b</span> 921600 bk7231n stock-dump.bin
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">/dev/cu.usbserial-B0010WQ0</code>是USB转TTL转换器在电脑上显示的串口设备，最后备份的固件大概有2.1M。</p>

<p>ltchiptool还有一个插件可以分析原厂固件，自动生成ESPHome配置文件，但是这个设备的固件我试了分析不成功，我们需要自己配置。</p>

<p><img src="../images/upk2esphome-plugin.png" alt="ltchiptool UPK2ESPHome Plugin" /></p>

<p>这个开关提供了4个引脚可以控制：</p>

<ul>
  <li>P7: LED指示灯，很亮的绿色，原厂固件是接通灯泡时会点亮</li>
  <li>P8: 按钮按下或者松开的状态</li>
  <li>P14: 继电器</li>
  <li>P16: 另外一个没那么亮的红色LED</li>
</ul>

<p>以下是一份示范配置：</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">esphome</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">example-switch</span>
  <span class="na">friendly_name</span><span class="pi">:</span> <span class="s">Example Switch</span>

<span class="na">bk72xx</span><span class="pi">:</span>
  <span class="na">board</span><span class="pi">:</span> <span class="s">generic-bk7231n-qfn32-tuya</span>

<span class="na">logger</span><span class="pi">:</span>

<span class="na">web_server</span><span class="pi">:</span>

<span class="na">captive_portal</span><span class="pi">:</span>

<span class="na">mdns</span><span class="pi">:</span>

<span class="na">api</span><span class="pi">:</span>

<span class="na">ota</span><span class="pi">:</span>

<span class="na">wifi</span><span class="pi">:</span>
  <span class="na">ssid</span><span class="pi">:</span> <span class="kt">!secret</span> <span class="s">wifi_ssid</span>
  <span class="na">password</span><span class="pi">:</span> <span class="kt">!secret</span> <span class="s">wifi_password</span>
  <span class="c1"># 设备连不上WiFi时会启动这个热点，默认的热点名就是上面的friendly_name，我们可以连上去更新WiFi信息</span>
  <span class="na">ap</span><span class="pi">:</span>
    <span class="na">password</span><span class="pi">:</span> <span class="s2">"</span><span class="s">T4hIwT0aCesq"</span>

<span class="c1"># 重启按钮</span>
<span class="na">button</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">restart</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Restart</span>
    <span class="na">id</span><span class="pi">:</span> <span class="s">restart_button</span>

<span class="na">debug</span><span class="pi">:</span>
  <span class="na">update_interval</span><span class="pi">:</span> <span class="s">30s</span>

<span class="na">text_sensor</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">debug</span>
    <span class="na">reset_reason</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">Reset Reason</span>
  <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">libretiny</span>
    <span class="na">version</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">LibreTiny Version</span>

<span class="na">sensor</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">uptime</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Uptime</span>
  <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">wifi_signal</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">WiFi</span><span class="nv"> </span><span class="s">Signal"</span>
    <span class="na">update_interval</span><span class="pi">:</span> <span class="s">60s</span>

<span class="na">uart</span><span class="pi">:</span>
  <span class="na">rx_pin</span><span class="pi">:</span> <span class="s">RX1</span>
  <span class="na">tx_pin</span><span class="pi">:</span> <span class="s">TX1</span>
  <span class="na">baud_rate</span><span class="pi">:</span> <span class="m">9600</span>

<span class="c1"># Register the Tuya MCU connection</span>
<span class="c1"># tuya:</span>

<span class="c1"># Pins:</span>
<span class="c1"># - P7: LED指示灯，很亮的绿色，原厂固件是接通灯泡时会点亮</span>
<span class="c1"># - P8: 按钮按下或者松开的状态</span>
<span class="c1"># - P14: 继电器</span>
<span class="c1"># - P16: 另外一个没那么亮的红色LED</span>

<span class="c1"># 继电器开关</span>
<span class="na">switch</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">gpio</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Relay"</span>
    <span class="na">id</span><span class="pi">:</span> <span class="s">relay</span>
    <span class="na">pin</span><span class="pi">:</span> <span class="s">P14</span>
    <span class="c1"># 同步LED指示灯状态</span>
    <span class="na">on_turn_on</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">light.turn_on</span><span class="pi">:</span> <span class="s">led_indicator</span>
    <span class="na">on_turn_off</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">light.turn_off</span><span class="pi">:</span> <span class="s">led_indicator</span>
    <span class="na">restore_mode</span><span class="pi">:</span> <span class="s">RESTORE_DEFAULT_OFF</span>

<span class="c1"># 使用红LED灯当ESPHome的状态</span>
<span class="na">status_led</span><span class="pi">:</span>
  <span class="na">pin</span><span class="pi">:</span>
    <span class="na">number</span><span class="pi">:</span> <span class="m">16</span>
    <span class="na">inverted</span><span class="pi">:</span> <span class="no">true</span> <span class="c1"># 这个开关的状态是反的</span>

<span class="c1"># LED指示灯</span>
<span class="na">light</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">binary</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">LED</span><span class="nv"> </span><span class="s">Indicator"</span>
    <span class="na">id</span><span class="pi">:</span> <span class="s">led_indicator</span>
    <span class="na">output</span><span class="pi">:</span> <span class="s">led_output</span>
    <span class="na">restore_mode</span><span class="pi">:</span> <span class="s">RESTORE_DEFAULT_OFF</span>

<span class="na">output</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">id</span><span class="pi">:</span> <span class="s">led_output</span>
    <span class="na">platform</span><span class="pi">:</span> <span class="s">gpio</span>
    <span class="na">pin</span><span class="pi">:</span>
      <span class="na">number</span><span class="pi">:</span> <span class="m">7</span>
      <span class="na">inverted</span><span class="pi">:</span> <span class="no">true</span>

<span class="c1"># 按钮状态</span>
<span class="na">binary_sensor</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">gpio</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Button"</span>
    <span class="na">pin</span><span class="pi">:</span>
      <span class="na">number</span><span class="pi">:</span> <span class="m">8</span>
      <span class="na">inverted</span><span class="pi">:</span> <span class="no">true</span>
    <span class="c1"># 短按接通电路</span>
    <span class="na">on_click</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">min_length</span><span class="pi">:</span> <span class="s">50ms</span>
      <span class="na">max_length</span><span class="pi">:</span> <span class="s">350ms</span>
      <span class="na">then</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">switch.toggle</span><span class="pi">:</span> <span class="s">relay</span>
    <span class="c1"># 长按重启</span>
    <span class="pi">-</span> <span class="na">min_length</span><span class="pi">:</span> <span class="s">5000ms</span>
      <span class="na">max_length</span><span class="pi">:</span> <span class="s">10000ms</span>
      <span class="na">then</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">button.press</span><span class="pi">:</span> <span class="s">restart_button</span>
</code></pre></div></div>

<p>在ESPHome里编译好<code class="language-plaintext highlighter-rouge">uf2</code>文件后，使用下面的命令行程序烧录到芯片里：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ltchiptool <span class="nt">-v</span> flash write <span class="nt">-d</span> /dev/cu.usbserial-B0010WQ0 <span class="nt">-b</span> 921600 example-switch.uf2
</code></pre></div></div>

<p>烧录好后等一会儿，开关就会连上WiFi，然后Home Assistant应该就能自动找到这个新的开关了。</p>

<p><img src="../images/gosund-sw5-in-ha.png" alt="Gosund SW5 in Home Assistant" /></p>]]></content><author><name>Guangming Mao</name></author><category term="blog" /><category term="esphome" /><category term="smarthome" /><category term="homeassistant" /><category term="bk7231n" /><summary type="html"><![CDATA[安装开关涉及强电，请务必注意人身安全并遵守当地法律法规！！！]]></summary></entry><entry xml:lang="zh"><title type="html">2022 年度回顾</title><link href="https://maoguangming.com/blog/wrap-up-2022.html" rel="alternate" type="text/html" title="2022 年度回顾" /><published>2023-01-04T00:36:00-08:00</published><updated>2023-01-04T00:36:00-08:00</updated><id>https://maoguangming.com/blog/wrap-up-2022</id><content type="html" xml:base="https://maoguangming.com/blog/wrap-up-2022.html"><![CDATA[<p>都2023年了，新年过去好几天了才想起来写个年终回顾，本来我也不是个喜欢发post的人，新年甚至没有发个朋友圈。过去的一年世界风云动荡，疫情，战争，经济衰退，闹剧，还好这边生活还算稳定，家人也还安好。</p>

<h2 id="生活">生活</h2>

<p>尘尘出生后，大部分时间是围着他转。过去一年依然如此，尘尘已经从年初蹒跚学步长到可以翻箱倒柜了，语言能力最近也终于突飞猛进，昨晚还在床上唱「一闪一闪亮晶晶」。</p>

<p>年初尘尘做了个切除<a href="https://www.stanfordchildrens.org/en/topic/default?id=dermoid-cyst-90-P02032">Dermoid Cyst</a>的小手术，当时看着他额角头发下3cm长的缝针口好心疼，还好尘尘就是别人口中的“天使尘尘”，手术完很快就不哭了，之后依然能吃能睡，恢复得还不错。</p>

<p>去年也开始给尘尘讲睡前故事，每天和苏苏轮流讲半个多小时，<del>希望他早日养成自己鸡自己的好习惯</del>。现在的小朋友好幸福啊，有各种各样的绘本，有些书我们都能津津有味看好久，最近尘尘最喜欢的是 Rotraut Susanne Berner 的 All Around Bustletown 系列，这套书细节十分丰富，可以翻来覆去给尘尘讲好多遍。</p>

<p>非常感谢岳父在前年7月我们鸡飞狗跳的时候来帮我们一把，当时边境还没开，他一个从没出远门的人，经由新加坡洗白，三番转机来西雅图，舟车劳顿，中途还进了两次海关小黑屋，牺牲特别大。他在这里一直帮忙到了3月，只可惜大部分时间是这里的雨季，没有带他多出去看看。尘尘和爷爷关系特别好，甚至爱屋及乌对其他小朋友的爷爷都更加亲近 🤣。</p>

<p>岳父回国后，尘尘就按计划去了离家不远的daycare。之前听说尘尘去daycare首先要生一个月病同步病毒库，一开始我们不以为然，以为尘尘长得结实，应该能免俗，然而现实好好教我们做了回人，他第一个月都在陆陆续续发烧，咳嗽，现在家中还常备几瓶泰诺，布洛芬。这也让我们更加深刻认识到尘尘是个普通人。尘尘刚开始去也不适应，每天哭得稀里哗啦，我们留了好多视频，印象最深刻的一个是尘尘一个人嘟着嘴站在窗户边落寞地看向外面，每次看起来都十分心疼，但是我们一直认为这是尘尘必须要跨出的一步，去daycare对尘尘发展也更好。现在好了，尘尘呆得很开心，每天daycare的报告上不是“happy today”就是“cheerful today”，早上去送的时候偶尔还会哭着要抱抱，基本上我们一出教室门就好了。</p>

<p>前房主不怎么管草坪，后面院子几乎快秃了，这个夏天心血来潮，把后面的草坪推了，买了车土铺上，重新种了新草。由于后院地势高，卡车上不去，土是用小推车一小车一小车推上去的，累得半死，但是看到新草慢慢发芽，渐渐长成绿油油的草坪（<del>在收到水费账单之前</del>）心情别提多欣慰。</p>

<p>夏天把后院前房主留下的烤炉清理出来了，其实也就冲了一下，几乎是新的。约朋友搞了好几次烧烤，大家一起在绿油油的新草坪旁串肉，烤肉，赶被烤肉吸引来的马蜂，夕阳西下，炊烟袅袅，十分安逸。顺手整理了一份<a href="./2022-10-23-summer-bbq-recipe.md">菜谱</a>，明年还可以继续。</p>

<p>除去烧烤，周末经常带着尘尘约各种小朋友在各种playground，公园，家里，海边，湖边，农场play date，尘尘年纪轻轻就有了好多小玩伴，不禁想起「郎骑竹马来，绕床弄青梅。同居长干里，两小无嫌猜。」这一年见证了尘尘第一次在海边泥巴里面挖贝壳，第一次在湖里划小船，第一次在回家山路上看夕阳，还有好多好多的第一次。</p>

<p>年底发现机票价格还行，帮妈妈买了机票来这边看看。5年没见了，印象中妈妈好像没那么唠叨 🤣。</p>

<h2 id="旅游">旅游</h2>

<p>经常和单身的朋友说，一定要趁着没有娃多出去旅游。去年只去了两次温哥华，一次波特兰。</p>

<p>第一次去温哥华是我们带尘尘单独去的，他超级兴奋，晚上也不好好睡，出去吃饭也到处跑，我和苏苏只能趁机吃几口，完全失去了众所周知去温哥华的最大乐趣——美食，第二天我们就电量耗尽草草回来了。第二次是和朋友家宝宝一起去的，小朋友互相消耗，我们倒是多逛了几个地方，还收获了一家叫「渔人」的好餐厅。</p>

<h2 id="工作">工作</h2>

<p>过去一年工作上最大的变化是换了个工作，从后端回到了客户端方向。</p>

<p>入职不久经历了人生第一次出差，去自由女神像🗽打了个卡。只是不巧，尘尘在我刚到纽约就开始发烧了，宝宝一发烧就很闹，辛苦苏苏了，我回来的晚上两人都已经快断绝母子关系了 🤣。</p>

<p>受经济大环境影响，在经过两年找工市场的疯狂后，年中各个公司开始冻结招聘，到年底我见证了一波前所未见的裁员潮，团队里两个朋友也受了影响，这是我毕业以后第一次如此近距离接触经济衰退。明天经济大概率还会继续下行，希望大家苟住。</p>

<h2 id="个人">个人</h2>

<p>宝宝出生之后，就几乎没有整段的时间，电影电视几乎不看了，游戏机也吃灰好久了。</p>

<p>今年看的电影，印象最深的有：</p>
<ul>
  <li>粤剧电影『白蛇传·情』，场景是真维美，戏曲是中国传统文化美学精神的集大成者，但是在现代文娱产品冲击下苟活不易，希望以后能有更好的发展。</li>
  <li>纯沪语文艺片『爱情神话』，再也不是小清新的年纪了，这种烟火气十足的中年人爱情片更能看进去。</li>
  <li>旧电影『剑雨』，特别喜欢的一部武侠片，居然是2010年的片子了。富含诗意的武打场景，一部充满了细碎生活场景的，非常不“武侠”的武侠片。</li>
</ul>

<p>书也看得少，除了给宝宝读的童话书绘本，就读完了这几本：</p>
<ul>
  <li>『1984』：终于看完了这部书，背脊发凉。曾经听说过一个说法，未来的世界，要么是『1984』的，要么是『美丽新世界』的。感觉人类到不了星辰大海就被把自己搞死。</li>
  <li>『筚路维艰』：现在读新中国初期的各种运动，气息十分熟悉。国家机器下，人就是螺丝钉，之后到改革开放，国退民进，让利于民后，情况才好一些。</li>
  <li>『长安的荔枝』：非常马亲王的小说，从小人物视角看历史，有趣。</li>
</ul>

<p>不过不好意思的说，今年消费得最多的文化产品是玄幻爽文。准备换工作的那段时间，压力比较大，经常开着喜马拉雅听书当背景音，垃圾网文听了好多本，听起来真解压，就是每本都听不长，听几章就腻了，各种套路，三观感人。那天看到有一个研究是研究网文主角性格与经济环境的关系，文艺作品真的是时代的剪影。</p>

<h2 id="展望">展望</h2>

<p>一个字，苟。希望家人都安好，希望国内的亲戚朋友挺过这一轮疫情。希望经济奇迹出现。</p>

<p>多看看书，定个小目标6本吧。</p>

<p>多运动。</p>

<p>和家人一起出去旅个游。</p>]]></content><author><name>Guangming Mao</name></author><category term="blog" /><category term="随笔" /><summary type="html"><![CDATA[都2023年了，新年过去好几天了才想起来写个年终回顾，本来我也不是个喜欢发post的人，新年甚至没有发个朋友圈。过去的一年世界风云动荡，疫情，战争，经济衰退，闹剧，还好这边生活还算稳定，家人也还安好。]]></summary></entry><entry xml:lang="zh"><title type="html">夏日烧烤菜谱</title><link href="https://maoguangming.com/blog/summer-bbq-recipe.html" rel="alternate" type="text/html" title="夏日烧烤菜谱" /><published>2022-10-23T13:15:00-07:00</published><updated>2022-10-23T13:15:00-07:00</updated><id>https://maoguangming.com/blog/summer-bbq-recipe</id><content type="html" xml:base="https://maoguangming.com/blog/summer-bbq-recipe.html"><![CDATA[<p>靈魂撒料：</p>

<p><img src="../images/bbq_powder.jpg" alt="靈魂撒料" /></p>

<p>以防停產，記錄一下撒料個配料：辣椒粉，芝麻，紫蘇，孜然，花生，味精，鹽，黃豆粉，洋蔥粉，白砂糖。</p>

<h2 id="蝦">蝦</h2>
<ol>
  <li>簽子穿好，擦乾水</li>
  <li>烤乾，刷油，撒調料</li>
  <li>小火慢烤至不透明</li>
</ol>

<h2 id="烤魚">烤魚</h2>
<ol>
  <li>魚從背部切開，改刀</li>
  <li>蔥一根，薑一小塊，花椒2g，八角2個，鹽25g，生抽15g，水800g，魚隔夜浸泡醃製</li>
  <li>烤魚夾子夾好</li>
  <li>小火慢烤至表皮酥脆</li>
</ol>

<h2 id="羊排">羊排</h2>
<ol>
  <li>costco羊排切開</li>
  <li>肉重12%的洋蔥，3%的撒料，冰箱隔夜醃製</li>
  <li>小火慢烤至內部溫度到63度</li>
</ol>

<h2 id="牛肉串">牛肉串</h2>
<ol>
  <li>牛肉切成粒</li>
  <li>肉重10%的洋蔥，3%的撒料，1%的糖，1%的胡椒粉，1%的生抽，1%的鹽，16%的水，打成糊，冰箱隔夜醃製</li>
  <li>甜椒切塊與牛肉同串</li>
  <li>小火慢烤至內部溫度到63度</li>
</ol>

<h2 id="金針菇粉絲">金針菇粉絲</h2>
<ol>
  <li>粉絲泡發，與金針菇，蒜茸置錫紙上烤熟</li>
</ol>

<h2 id="五花肉年糕">五花肉，年糕</h2>
<ol>
  <li>五花肉切片置於錫紙上慢火烤至金黃</li>
  <li>五花肉取出放鹽和撒料</li>
  <li>錫紙上放年糕烤至酥脆</li>
</ol>

<h2 id="雞翅">雞翅</h2>
<ol>
  <li>奧爾良雞翅醃料說明醃製</li>
  <li>小火慢烤至內部溫度到74度</li>
</ol>

<h2 id="烤鱿鱼">烤鱿鱼</h2>
<ol>
  <li>撒料加油配成酱料</li>
  <li>鱿鱼切块/整条剪好，刷酱</li>
  <li>烤箱200度烤五分钟</li>
  <li>拿出来翻面刷酱，再烤5分钟</li>
  <li>拿出来翻面刷酱，再烤3分钟</li>
  <li>拿出来翻面刷酱，再烤3分钟</li>
</ol>

<hr />

<h2 id="參考">參考</h2>
<ol>
  <li>
    <p>賴皮猴愛美食 <a href="https://youtu.be/u8fBaEHkLy4">https://youtu.be/u8fBaEHkLy4</a></p>

    <p>肉部分：肥瘦相间牛肉500g 、羊排或羊腿肉500g。</p>

    <p>牛肉腌料部分：洋葱丝50g、孜然粉5g、十三香1g、白糖4g、白胡椒粉5g、 蒜粉5g、生抽5g、盐6g、鸡精4g、小苏打0.5g、黄豆粉20g、水80g</p>

    <p>羊肉腌料部分： 洋葱丝60g、一个蛋清、蚝油10g、盐6g</p>

    <p>肉串撒料部份：半颗粒然50g、芝麻10g、秘制鲜香小料10g</p>

    <p>秘制鲜香料部分： 鸡精100g、 味精30g、陈有香肉香粉5g、四宝浓缩鲜香粉5g、 十三香1g</p>
  </li>
  <li>
    <p>小高姐 烤魚 <a href="https://youtu.be/3GeG8wzJXdU">https://youtu.be/3GeG8wzJXdU</a></p>
  </li>
</ol>]]></content><author><name>Guangming Mao</name></author><category term="blog" /><category term="菜谱" /><summary type="html"><![CDATA[靈魂撒料：]]></summary></entry><entry xml:lang="zh"><title type="html">詹橋話拼音方案</title><link href="https://maoguangming.com/blog/jizrsa-pinyin.html" rel="alternate" type="text/html" title="詹橋話拼音方案" /><published>2019-05-02T23:41:00-07:00</published><updated>2019-05-02T23:41:00-07:00</updated><id>https://maoguangming.com/blog/jizrsa-pinyin</id><content type="html" xml:base="https://maoguangming.com/blog/jizrsa-pinyin.html"><![CDATA[<p>前段時間因緣巧合見到一片寫詹橋話的論文，裏面詳細說明了詹橋話的各個方面：聲母韻母聲調，文白異讀，與切韻音系的對比等等。論文對我啓發頗大，我由此稍微學習了一下國際音標，中古漢語，音韻學之類的東西，最喜歡學習這種沒什麼用的知識了🙄️。</p>

<p>看完之後仔細想來，家鄉的方言作爲弱勢方言，語音正在受漢語拼音的侵噬，其實每個方言都是如此。於是心裏有了一個小小的願望，制定爲自己的方言定一套拼音方案，並且爲方言正字正音，算是爲家鄉文化做一點微小的工作。
詹橋地處湖南湖北江西三省交界處，屬於贛語到湘語的過渡區，這裏屬於所謂的“濁音走廊”，在當今大部分方言的濁音都已經清化的大背景下，詹橋話裏廣泛存在着濁音聲母。下面是筆者所定的詹橋話拼音方案，由於漢語拼音方案廣爲人知，這套方案大體與漢語拼音方案類似。</p>

<h2 id="聲母部分">聲母部分</h2>

<p>聲母大部分采用與漢語拼音相同的字母，而不用國際音標寫法，以方便對漢語拼音方案的朋友們學習，但是具體發音不太相同。下文中均用方括號<code class="language-plaintext highlighter-rouge">[]</code>標註國際音標。</p>

<p>比如雙唇塞音，漢語拼音裡有兩個，分別是<code class="language-plaintext highlighter-rouge">b</code>/<code class="language-plaintext highlighter-rouge">p</code>，對應的國際音標為<code class="language-plaintext highlighter-rouge">[p]</code>/<code class="language-plaintext highlighter-rouge">[pʰ]</code>，而詹橋話裡沒有送氣清音，只有清音和濁音，對應的國際音標為<code class="language-plaintext highlighter-rouge">[p]</code>/<code class="language-plaintext highlighter-rouge">[b]</code>，由於清音<code class="language-plaintext highlighter-rouge">[p]</code>與漢語拼音發音相同，故本方案沿用漢語拼音<code class="language-plaintext highlighter-rouge">b</code>表示清音，而使用<code class="language-plaintext highlighter-rouge">p</code>表示濁音。同理，齒齦塞音<code class="language-plaintext highlighter-rouge">d</code>/<code class="language-plaintext highlighter-rouge">t</code>，齒齦塞擦音<code class="language-plaintext highlighter-rouge">z</code>/<code class="language-plaintext highlighter-rouge">c</code>，齦顎塞擦音<code class="language-plaintext highlighter-rouge">j</code>/<code class="language-plaintext highlighter-rouge">q</code>，軟顎塞音<code class="language-plaintext highlighter-rouge">g</code>/<code class="language-plaintext highlighter-rouge">k</code>等類似，都是前者爲清音，後者爲濁音，而不是讀漢語拼音里讀送氣音。</p>

<p>邊音<code class="language-plaintext highlighter-rouge">[l]</code>的洪音字與鼻音<code class="language-plaintext highlighter-rouge">[n]</code>在詹橋話里已合流，邊音<code class="language-plaintext highlighter-rouge">[l]</code>的細音字已派入濁齒齦塞音<code class="language-plaintext highlighter-rouge">[d]</code>，故本方案都拼做<code class="language-plaintext highlighter-rouge">n</code>，不設單獨的<code class="language-plaintext highlighter-rouge">l</code>。此外詹橋話裏沒有捲舌音<code class="language-plaintext highlighter-rouge">zh</code>、<code class="language-plaintext highlighter-rouge">ch</code>、<code class="language-plaintext highlighter-rouge">sh</code>、<code class="language-plaintext highlighter-rouge">r</code>，相比與漢語拼音，多了齦顎鼻音<code class="language-plaintext highlighter-rouge">nj</code> <code class="language-plaintext highlighter-rouge">[ȵ]</code>以及軟顎鼻音<code class="language-plaintext highlighter-rouge">ng</code> <code class="language-plaintext highlighter-rouge">[ŋ]</code>。</p>

<h3 id="聲母表">聲母表</h3>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>清塞音</th>
      <th>濁塞音</th>
      <th>清塞擦音</th>
      <th>濁塞擦音‍‍</th>
      <th>擦音‍‍</th>
      <th>鼻音</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>‍‍‍‍ 雙脣音</td>
      <td><code class="language-plaintext highlighter-rouge">b</code> <code class="language-plaintext highlighter-rouge">[p]</code> <br />波</td>
      <td><code class="language-plaintext highlighter-rouge">p</code> <code class="language-plaintext highlighter-rouge">[b]</code> <br />坡</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">m</code> <code class="language-plaintext highlighter-rouge">[m]</code> <br />摸</td>
    </tr>
    <tr>
      <td>脣齒音</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">f</code> <code class="language-plaintext highlighter-rouge">[f/ɸ]</code>/<code class="language-plaintext highlighter-rouge">[v/β]</code> <br />佛/符</td>
      <td> </td>
    </tr>
    <tr>
      <td>舌頭音</td>
      <td><code class="language-plaintext highlighter-rouge">d</code> <code class="language-plaintext highlighter-rouge">[t]</code> <br />德</td>
      <td><code class="language-plaintext highlighter-rouge">t</code> <code class="language-plaintext highlighter-rouge">[d]</code> <br />特</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">n</code> <code class="language-plaintext highlighter-rouge">[n]</code> <br />呢/來</td>
    </tr>
    <tr>
      <td>齒頭音‍</td>
      <td>‍</td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">z</code> <code class="language-plaintext highlighter-rouge">[ts]</code> <br />子</td>
      <td><code class="language-plaintext highlighter-rouge">c</code> <code class="language-plaintext highlighter-rouge">[dz]</code> <br />此</td>
      <td><code class="language-plaintext highlighter-rouge">s</code> <code class="language-plaintext highlighter-rouge">[s]</code> <br />四</td>
      <td> </td>
    </tr>
    <tr>
      <td>正齒音</td>
      <td> </td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">j</code> <code class="language-plaintext highlighter-rouge">[tɕ]</code> <br /> 吉</td>
      <td><code class="language-plaintext highlighter-rouge">q</code> <code class="language-plaintext highlighter-rouge">[dʑ]</code> <br />七</td>
      <td><code class="language-plaintext highlighter-rouge">x</code> <code class="language-plaintext highlighter-rouge">[ɕ]</code> <br />西</td>
      <td> </td>
    </tr>
    <tr>
      <td>牙音</td>
      <td><code class="language-plaintext highlighter-rouge">g</code> <code class="language-plaintext highlighter-rouge">[k/q]</code> <br />哥</td>
      <td><code class="language-plaintext highlighter-rouge">k</code> <code class="language-plaintext highlighter-rouge">[g/ɢ]</code> <br />可</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">ng</code> <code class="language-plaintext highlighter-rouge">[ŋ]</code> <br />我</td>
    </tr>
    <tr>
      <td>喉音</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">h</code> <code class="language-plaintext highlighter-rouge">[x]</code>/<code class="language-plaintext highlighter-rouge">[ɣ]</code> <br />好/紅</td>
      <td> </td>
    </tr>
    <tr>
      <td>‍ 半齒音</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">nj</code> <code class="language-plaintext highlighter-rouge">[ȵ]</code> <br />女</td>
    </tr>
  </tbody>
</table>

<p>此外有零聲母<code class="language-plaintext highlighter-rouge">y</code>/<code class="language-plaintext highlighter-rouge">w</code>。</p>

<h2 id="韻母部分">韻母部分</h2>

<p>韻母和漢語拼音有些許重合，撮口呼<code class="language-plaintext highlighter-rouge">ü</code>不好輸入，所以本方案寫作<code class="language-plaintext highlighter-rouge">yu</code>。</p>

<p>詹橋話保留了漢語的入聲，但是中古的<code class="language-plaintext highlighter-rouge">-p</code>、<code class="language-plaintext highlighter-rouge">-k</code>、<code class="language-plaintext highlighter-rouge">-t</code>入聲韻尾都退化成了喉塞音韻尾<code class="language-plaintext highlighter-rouge">[ʔ]</code>，本方案拼做<code class="language-plaintext highlighter-rouge">-q</code>。詹橋話裏有三個輔音可自成音節<code class="language-plaintext highlighter-rouge">m</code>、<code class="language-plaintext highlighter-rouge">n</code>、<code class="language-plaintext highlighter-rouge">ng</code>。</p>

<p>舌尖前不圓唇元音<code class="language-plaintext highlighter-rouge">[ɿ]</code>在漢語拼音中拼<code class="language-plaintext highlighter-rouge">z</code>、<code class="language-plaintext highlighter-rouge">c</code>、<code class="language-plaintext highlighter-rouge">s</code>，寫作<code class="language-plaintext highlighter-rouge">i</code>，如子、詞、思。爲作區分，本方案寫作<code class="language-plaintext highlighter-rouge">r</code>。</p>

<p>齊齒呼的韻母在沒有聲母的時候，遵從漢語拼音的習慣，寫作<code class="language-plaintext highlighter-rouge">yi</code>、<code class="language-plaintext highlighter-rouge">ya</code>、<code class="language-plaintext highlighter-rouge">ye</code>、<code class="language-plaintext highlighter-rouge">yao</code>、<code class="language-plaintext highlighter-rouge">you</code>、<code class="language-plaintext highlighter-rouge">yan</code>、<code class="language-plaintext highlighter-rouge">yin</code>、<code class="language-plaintext highlighter-rouge">yang</code>、<code class="language-plaintext highlighter-rouge">yong</code>、<code class="language-plaintext highlighter-rouge">ying</code>、<code class="language-plaintext highlighter-rouge">yiq</code>、<code class="language-plaintext highlighter-rouge">yaq</code>、<code class="language-plaintext highlighter-rouge">yoq</code>、<code class="language-plaintext highlighter-rouge">yeq</code>、<code class="language-plaintext highlighter-rouge">youq</code>。</p>

<p>合口呼的韻母在沒有聲母的時候，遵從漢語拼音的習慣，寫作<code class="language-plaintext highlighter-rouge">wu</code>、<code class="language-plaintext highlighter-rouge">wa</code>、<code class="language-plaintext highlighter-rouge">we</code>、<code class="language-plaintext highlighter-rouge">wai</code>、<code class="language-plaintext highlighter-rouge">wei</code>、<code class="language-plaintext highlighter-rouge">wan</code>、<code class="language-plaintext highlighter-rouge">won</code>、<code class="language-plaintext highlighter-rouge">wen</code>、<code class="language-plaintext highlighter-rouge">wong</code>、<code class="language-plaintext highlighter-rouge">weng</code>、<code class="language-plaintext highlighter-rouge">wuq</code>、<code class="language-plaintext highlighter-rouge">waq</code>、<code class="language-plaintext highlighter-rouge">woq</code>、<code class="language-plaintext highlighter-rouge">weq</code>、<code class="language-plaintext highlighter-rouge">weiq</code>。</p>

<p>撮口呼的韻母跟<code class="language-plaintext highlighter-rouge">j</code>、<code class="language-plaintext highlighter-rouge">q</code>、<code class="language-plaintext highlighter-rouge">x</code>、<code class="language-plaintext highlighter-rouge">nj</code>拼的時候，遵從漢語拼音的習慣，拼做<code class="language-plaintext highlighter-rouge">ju</code>、<code class="language-plaintext highlighter-rouge">qu</code>、<code class="language-plaintext highlighter-rouge">xu</code>、<code class="language-plaintext highlighter-rouge">nju</code>、<code class="language-plaintext highlighter-rouge">njui</code>，省略<code class="language-plaintext highlighter-rouge">y</code>。</p>

<p><code class="language-plaintext highlighter-rouge">iou</code>、<code class="language-plaintext highlighter-rouge">uei</code>、<code class="language-plaintext highlighter-rouge">uen</code>、<code class="language-plaintext highlighter-rouge">iouq</code>、<code class="language-plaintext highlighter-rouge">ueiq</code>前面加聲母的時候，遵從漢語拼音的習慣，寫作<code class="language-plaintext highlighter-rouge">iu</code>、<code class="language-plaintext highlighter-rouge">ui</code>、<code class="language-plaintext highlighter-rouge">un</code>、<code class="language-plaintext highlighter-rouge">iuq</code>、<code class="language-plaintext highlighter-rouge">uiq</code>。例如<code class="language-plaintext highlighter-rouge">tiu</code> 流、<code class="language-plaintext highlighter-rouge">gui</code> 歸、<code class="language-plaintext highlighter-rouge">gun</code> 滾等。</p>

<p><code class="language-plaintext highlighter-rouge">yuei</code>可簡寫成<code class="language-plaintext highlighter-rouge">yui</code>。</p>

<p><code class="language-plaintext highlighter-rouge">ieng</code>可簡寫成<code class="language-plaintext highlighter-rouge">ing</code>。</p>

<h3 id="韻母表">韻母表</h3>

<table>
  <thead>
    <tr>
      <th>開口呼</th>
      <th>齊齒呼</th>
      <th>合口呼</th>
      <th>撮口呼</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">r</code> <code class="language-plaintext highlighter-rouge">[ɿ]</code> <br />子</td>
      <td><code class="language-plaintext highlighter-rouge">i</code> <code class="language-plaintext highlighter-rouge">[i]</code> <br />衣</td>
      <td><code class="language-plaintext highlighter-rouge">u</code> <code class="language-plaintext highlighter-rouge">[u]</code> <br />烏</td>
      <td><code class="language-plaintext highlighter-rouge">yu</code> <code class="language-plaintext highlighter-rouge">[y]</code> <br />魚</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">a</code> <code class="language-plaintext highlighter-rouge">[ä]</code>阿</td>
      <td><code class="language-plaintext highlighter-rouge">ia</code> <code class="language-plaintext highlighter-rouge">[iä]</code> <br />丫</td>
      <td><code class="language-plaintext highlighter-rouge">ua</code> <code class="language-plaintext highlighter-rouge">[uä]</code> <br />哇</td>
      <td><code class="language-plaintext highlighter-rouge">yua</code> <code class="language-plaintext highlighter-rouge">[yä]</code> <br />靴</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">o</code> <code class="language-plaintext highlighter-rouge">[ɔ]</code> <br />哦</td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">e</code> <code class="language-plaintext highlighter-rouge">[e]</code> <br />勾</td>
      <td><code class="language-plaintext highlighter-rouge">ie</code> <code class="language-plaintext highlighter-rouge">[ie]</code> <br />椰</td>
      <td><code class="language-plaintext highlighter-rouge">ue</code> <code class="language-plaintext highlighter-rouge">[ue]</code> <br /></td>
      <td><code class="language-plaintext highlighter-rouge">yue</code> <code class="language-plaintext highlighter-rouge">[ye]</code> <br /></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">er</code> <code class="language-plaintext highlighter-rouge">[ɵ]</code> <br />兒</td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ai</code> <code class="language-plaintext highlighter-rouge">[æ]</code> <br />呆</td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">uai</code> <code class="language-plaintext highlighter-rouge">[uæ]</code> <br />歪</td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ei</code> <code class="language-plaintext highlighter-rouge">[ei]</code> <br />飛</td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">uei</code> <code class="language-plaintext highlighter-rouge">[uei]</code> <br />未</td>
      <td><code class="language-plaintext highlighter-rouge">yuei</code> <code class="language-plaintext highlighter-rouge">[yei]</code> <br />蕊</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ao</code> <code class="language-plaintext highlighter-rouge">[ɑu]</code> <br />高</td>
      <td><code class="language-plaintext highlighter-rouge">iao</code> <code class="language-plaintext highlighter-rouge">[iɑu]</code> <br />腰</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ou</code> <code class="language-plaintext highlighter-rouge">[əu]</code> <br />歐</td>
      <td><code class="language-plaintext highlighter-rouge">iou</code> <code class="language-plaintext highlighter-rouge">[iəu]</code> <br />又</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">an</code> <code class="language-plaintext highlighter-rouge">[an]</code> <br />山</td>
      <td><code class="language-plaintext highlighter-rouge">ian</code> <code class="language-plaintext highlighter-rouge">[iɛn]</code> <br />煙</td>
      <td><code class="language-plaintext highlighter-rouge">uan</code> <code class="language-plaintext highlighter-rouge">[uan]</code> <br />萬</td>
      <td><code class="language-plaintext highlighter-rouge">yuan</code> <code class="language-plaintext highlighter-rouge">[yɐn]</code> <br />元</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">on</code> <code class="language-plaintext highlighter-rouge">[ɞn]</code> <br />安</td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">uon</code> <code class="language-plaintext highlighter-rouge">[uɞn]</code> <br />官</td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">en</code> <code class="language-plaintext highlighter-rouge">[ən]</code> <br />真</td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">uen</code> <code class="language-plaintext highlighter-rouge">[uən]</code> <br />文</td>
      <td> </td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">in</code> <code class="language-plaintext highlighter-rouge">[in]</code> <br />金</td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">yun</code> <code class="language-plaintext highlighter-rouge">[yn]</code> <br />云</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ang</code> <code class="language-plaintext highlighter-rouge">[aŋ]</code> <br />生</td>
      <td><code class="language-plaintext highlighter-rouge">iang</code> <code class="language-plaintext highlighter-rouge">[iaŋ]</code> <br />平</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ong</code> <code class="language-plaintext highlighter-rouge">[ɔŋ]</code> <br />幫</td>
      <td><code class="language-plaintext highlighter-rouge">iong</code> <code class="language-plaintext highlighter-rouge">[iɔŋ]</code> <br />兩</td>
      <td><code class="language-plaintext highlighter-rouge">uong</code> <code class="language-plaintext highlighter-rouge">[uɔŋ]</code> <br />光</td>
      <td><code class="language-plaintext highlighter-rouge">yuong</code> <code class="language-plaintext highlighter-rouge">[yɔŋ]</code> <br />縈</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">eng</code> <code class="language-plaintext highlighter-rouge">[əŋ]</code> <br />公</td>
      <td><code class="language-plaintext highlighter-rouge">ieng</code> <code class="language-plaintext highlighter-rouge">[iəŋ]</code> <br />兄</td>
      <td><code class="language-plaintext highlighter-rouge">ueng</code> <code class="language-plaintext highlighter-rouge">[uəŋ]</code> <br />翁</td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">rq</code> <code class="language-plaintext highlighter-rouge">[ɿʔ]</code> <br />直</td>
      <td><code class="language-plaintext highlighter-rouge">iq</code> <code class="language-plaintext highlighter-rouge">[iʔ]</code> <br />一</td>
      <td><code class="language-plaintext highlighter-rouge">uq</code> <code class="language-plaintext highlighter-rouge">[uʔ]</code> <br />屋</td>
      <td><code class="language-plaintext highlighter-rouge">yuq</code> <code class="language-plaintext highlighter-rouge">[yʔ]</code> <br /></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">aq</code> <code class="language-plaintext highlighter-rouge">[äʔ]</code> <br /></td>
      <td><code class="language-plaintext highlighter-rouge">iaq</code> <code class="language-plaintext highlighter-rouge">[iäʔ]</code> <br />壓</td>
      <td><code class="language-plaintext highlighter-rouge">uaq</code> <code class="language-plaintext highlighter-rouge">[uäʔ]</code> <br />滑</td>
      <td><code class="language-plaintext highlighter-rouge">yuaq</code> <code class="language-plaintext highlighter-rouge">[yäʔ]</code> <br /></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">oq</code> <code class="language-plaintext highlighter-rouge">[ɔʔ]</code> <br />鴿</td>
      <td><code class="language-plaintext highlighter-rouge">ioq</code> <code class="language-plaintext highlighter-rouge">[iɔʔ]</code> <br />藥</td>
      <td><code class="language-plaintext highlighter-rouge">uoq</code> <code class="language-plaintext highlighter-rouge">[uɔʔ]</code> <br />沃</td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">erq</code> <code class="language-plaintext highlighter-rouge">[ɵʔ]</code> <br />割</td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">eq</code> <code class="language-plaintext highlighter-rouge">[eʔ]</code> <br />黑</td>
      <td><code class="language-plaintext highlighter-rouge">ieq</code> <code class="language-plaintext highlighter-rouge">[ieʔ]</code> <br />結</td>
      <td><code class="language-plaintext highlighter-rouge">ueq</code> <code class="language-plaintext highlighter-rouge">[ueʔ]</code> <br />國</td>
      <td><code class="language-plaintext highlighter-rouge">yueq</code> <code class="language-plaintext highlighter-rouge">[yeʔ]</code> <br />月</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">aiq</code> <code class="language-plaintext highlighter-rouge">[æʔ]</code> <br />殺</td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td> </td>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">ueiq</code> <code class="language-plaintext highlighter-rouge">[ueiʔ]</code> <br />骨</td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ouq</code> <code class="language-plaintext highlighter-rouge">[əuʔ]</code> <br />粥</td>
      <td><code class="language-plaintext highlighter-rouge">iouq</code> <code class="language-plaintext highlighter-rouge">[iəuʔ]</code> <br />菊</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">m</code> <code class="language-plaintext highlighter-rouge">[m̩]</code> <br />姆</td>
      <td><code class="language-plaintext highlighter-rouge">n</code> <code class="language-plaintext highlighter-rouge">[n̩]</code> <br />你</td>
      <td><code class="language-plaintext highlighter-rouge">ng</code> <code class="language-plaintext highlighter-rouge">[ŋ̍]</code> <br />蜈</td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h2 id="聲調部分">聲調部分</h2>

<p>詹橋話有6個聲調，此方案用1~6標識調類，由於入聲韻有q結尾，可以省略調類6。</p>

<table>
  <thead>
    <tr>
      <th>調類</th>
      <th>調值</th>
      <th>例字</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>陰平</td>
      <td>44</td>
      <td>爸 媽 花 沙 家 哈</td>
    </tr>
    <tr>
      <td>陽平</td>
      <td>13</td>
      <td>爬 麻 華 茶 床 窮</td>
    </tr>
    <tr>
      <td>上聲</td>
      <td>42</td>
      <td>普 母 虎 鼓 苦 丑</td>
    </tr>
    <tr>
      <td>陰去</td>
      <td>35</td>
      <td>怕 化 卦 跨 曬 宋</td>
    </tr>
    <tr>
      <td>陽去</td>
      <td>22</td>
      <td>步 父 近 亥 冇 亮</td>
    </tr>
    <tr>
      <td>入聲</td>
      <td>5</td>
      <td>黑 月 殺 骨 藥 屋</td>
    </tr>
  </tbody>
</table>

<h2 id="例子">例子</h2>
<p>由於漢語拼音沒有入聲，用詹橋話拼音讀有些古詩，尤其是入聲韻的古詩，會十分押韻。比如：</p>

<div style="text-align: center" class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jiong1 xieq6
江雪
tiou3 zeng1 yuan2
柳宗元

qian1 san1 njiao3 fei1 qieq6
千山鸟飞绝
wan5 jin4 njin2 zeng1 meq6
万径人踪灭
gu1 zou1 soq6 tiq6 eng1
孤舟蓑笠翁
touq6 diao4 hon2 jiong1 xieq6
独钓寒江雪
</code></pre></div></div>]]></content><author><name>Guangming Mao</name></author><category term="blog" /><category term="方言" /><category term="汉语" /><category term="拼音" /><summary type="html"><![CDATA[前段時間因緣巧合見到一片寫詹橋話的論文，裏面詳細說明了詹橋話的各個方面：聲母韻母聲調，文白異讀，與切韻音系的對比等等。論文對我啓發頗大，我由此稍微學習了一下國際音標，中古漢語，音韻學之類的東西，最喜歡學習這種沒什麼用的知識了🙄️。]]></summary></entry><entry xml:lang="zh"><title type="html">雞子石</title><link href="https://maoguangming.com/blog/jizrsaq.html" rel="alternate" type="text/html" title="雞子石" /><published>2019-04-24T17:25:00-07:00</published><updated>2019-04-24T17:25:00-07:00</updated><id>https://maoguangming.com/blog/jizrsaq</id><content type="html" xml:base="https://maoguangming.com/blog/jizrsaq.html"><![CDATA[<p><img src="../images/jizrsaq.jpeg" alt="雞子石" /></p>

<p>漂泊經年，每每想起老家，就想起門對面山上的雞子石。</p>

<p>雞子石是一塊略顯光禿的石頭，從老家門口望去，圓鼓鼓的，像極了寫禿的鉛筆頭。小時候一直以為它背面也是這般模樣，直到有一次坐摩托車去山那邊的大姑媽家，才發現背面的雞子石坍塌了一塊，整個雞子石從側面看去居然是尖尖的。石頂上遠看倒有些植被，高中的時候和好友爬山，上去看過一次，上面是一人多高的灌木叢，密密麻麻的卻沒甚葉子，估計是被常年的山風吹的吧。</p>

<p>前段時間偶爾發現一本乾隆年間的《岳州府志》，心血來潮翻閱了一會兒，終於讓我在山川志的部分發現了雞子石的記載：“大雲山在縣東北一百二十里，盤旋七十餘峯，上有龍湫，又有石廟祀真武神并宋通判石光像，旁有雞子石，可容千人，明末郡人避亂於此，張李二賊環攻經年不破，半隸臨湘界”。第一次在歷史文獻中看到它的記載，當即在微信上分享給了爸媽。</p>

<p><img src="../images/dayunshan.jpeg" alt="大雲山" /></p>

<p>從小學起，為了就近上個好學校，爸媽就帶我住在二姑媽家。此後輾轉各地上學，除了初中在老家山下中學讀的那半年，再不曾在老家長住。高中畢業後北上去了北京，老家在記憶中再也沒有了初春三月的梅雨和金燦燦的稻田。大學畢業後飛到地球另外一邊求職，山高海遠再加上簽證身份問題，回家更是成了一種奢望。希望以後能多回家看看，留此文聊慰鄉情。</p>]]></content><author><name>Guangming Mao</name></author><category term="blog" /><category term="方言" /><category term="汉语" /><category term="随笔" /><summary type="html"><![CDATA[]]></summary></entry><entry xml:lang="zh"><title type="html">搬砖的时候发现一些关于砖 (block) 的问题</title><link href="https://maoguangming.com/blog/ref-in-block.html" rel="alternate" type="text/html" title="搬砖的时候发现一些关于砖 (block) 的问题" /><published>2018-07-27T11:17:00-07:00</published><updated>2018-07-27T11:17:00-07:00</updated><id>https://maoguangming.com/blog/ref-in-block</id><content type="html" xml:base="https://maoguangming.com/blog/ref-in-block.html"><![CDATA[<blockquote>
  <p>搬运自 <a href="https://zhuanlan.zhihu.com/p/40663395">https://zhuanlan.zhihu.com/p/40663395</a></p>
</blockquote>

<p>最近在微博上看到一个关于 block 的问题，有这么一段代码：</p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">typedef</span> <span class="kt">int</span><span class="p">(</span><span class="o">^</span><span class="n">Block</span><span class="p">)(</span><span class="kt">void</span><span class="p">);</span>
    <span class="n">Block</span> <span class="n">blocks</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span>
    <span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">blocks</span><span class="p">[</span><span class="nf">i</span><span class="p">]</span> <span class="o">=</span> <span class="o">^</span><span class="p">{</span>
            <span class="k">return</span> <span class="n">i</span><span class="p">;</span>
        <span class="p">};</span>
    <span class="p">}</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">blocks</span><span class="p">[</span><span class="nf">i</span><span class="p">]());</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>这段代码在 ARC 和 MRC 模式下分别会输出什么？答案是：ARC 模式下面会输出 <code class="language-plaintext highlighter-rouge">012</code>，而 MRC 模式下会输出 <code class="language-plaintext highlighter-rouge">222</code>。</p>

<h2 id="mrc-模式下的-block">MRC 模式下的 block</h2>

<p>我们先来讨论 MRC 模式下这段代码的行为，关于 block 的实现细节，可以参考这篇文章：<a href="http://clang.llvm.org/docs/Block-ABI-Apple.html">Block Implementation Specification</a>。从中我们可以看到编译器会生成一些描述这个 block 的 struct，由于我们引用了栈上的局部变量 i，这个 block 会存在于栈内存里（而不是全局内存里）。其实这 3 个 block 指针都指向栈上面同一片内存，每次循环唯一修改的只是这个 block 里面捕获的变量 i 的副本，循环完后，block 里面保存的副本值为 <code class="language-plaintext highlighter-rouge">2</code>，所以 MRC 模式下输出 3 个 <code class="language-plaintext highlighter-rouge">2</code>。</p>

<p>道理不难，我倒是很好奇编译器到底生成了什么样的代码，带着这个疑惑，我研究了一下上面代码对应的汇编代码，为了看懂这个汇编，我还临时抱佛脚，看了一篇<a href="https://web.archive.org/web/20170222021931/https://www3.nd.edu/~dthain/courses/cse40243/fall2015/intel-intro.html">汇编的介绍</a>。</p>

<p>首先生成 MRC 模式下的汇编代码：</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clang <span class="nt">-fno-stack-protector</span> <span class="nt">-fno-objc-arc</span> <span class="nt">-S</span> blocktest.m <span class="nt">-o</span> blocktest_mrc.s
</code></pre></div></div>

<p>因为主要区别在于 3 个 <code class="language-plaintext highlighter-rouge">blocks[i]</code> 赋值，我们重点看这一部分。这一段汇编代码结合上述 block 实现细节那篇文章的 <a href="https://clang.llvm.org/docs/Block-ABI-Apple.html#imported-variables">Imported Variables</a> 这一节看有奇效：</p>

<p>根据文章，我们的 block 大概会编译成：</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">__block_literal_tmp</span> <span class="p">{</span>
    <span class="kt">void</span> <span class="o">*</span><span class="n">isa</span><span class="p">;</span>  <span class="c1">// &amp;__NSConcreteStackBlock</span>
    <span class="kt">int</span> <span class="n">flags</span><span class="p">;</span>  <span class="c1">// 0xC0000000</span>
    <span class="kt">int</span> <span class="n">reserved</span><span class="p">;</span>  <span class="c1">// 0 </span>
    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">invoke</span><span class="p">)(</span><span class="k">struct</span> <span class="n">__block_literal_2</span> <span class="o">*</span><span class="p">);</span>
    <span class="k">struct</span> <span class="n">__block_descriptor_2</span> <span class="o">*</span><span class="n">descriptor</span><span class="p">;</span>
    <span class="k">const</span> <span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>我摘取了对应的汇编代码分享一下：</p>

<div class="language-armasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">...</span>
<span class="nl">_main</span><span class="err">:</span>
  <span class="nb">pushq</span> <span class="o">%</span><span class="nv">rbp</span>
  <span class="nb">movq</span>  <span class="o">%</span><span class="nv">rsp</span><span class="o">,</span> <span class="o">%</span><span class="nv">rbp</span>
  <span class="nb">subq</span>  <span class="err">$</span><span class="mi">80</span><span class="o">,</span> <span class="o">%</span><span class="nv">rsp</span>                       <span class="o">##</span> <span class="err">栈上留出</span> <span class="mi">80</span> <span class="nv">bytes</span>
  <span class="nb">movl</span>  <span class="err">$</span><span class="mi">0</span><span class="o">,</span> <span class="o">-</span><span class="mi">4</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>
  <span class="nb">movl</span>  <span class="err">$</span><span class="mi">0</span><span class="o">,</span> <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>                   <span class="o">##</span> <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span> <span class="err">存的是局部变量</span> <span class="nv">i</span>
<span class="nl">LBB0_1</span><span class="err">:</span>                                 <span class="err">#</span><span class="k">#</span> <span class="err">第一个循环</span>
  <span class="nb">cmpl</span>  <span class="err">$</span><span class="mi">3</span><span class="o">,</span> <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>
  <span class="nb">jge</span> <span class="nv">LBB0_4</span>                            <span class="o">##</span> <span class="nv">i</span> <span class="o">&gt;=</span> <span class="mi">3</span> <span class="err">则跳出循环</span>

  <span class="nb">leaq</span>  <span class="o">-</span><span class="mi">72</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">),</span> <span class="o">%</span><span class="nv">rax</span>                 <span class="o">##</span> <span class="nv">block</span> <span class="err">的地址</span>
  <span class="nb">leaq</span>  <span class="nv">___block_descriptor_tmp</span><span class="o">(%</span><span class="nv">rip</span><span class="o">),</span> <span class="o">%</span><span class="nv">rcx</span>
  <span class="nb">leaq</span>  <span class="nv">___main_block_invoke</span><span class="o">(%</span><span class="nv">rip</span><span class="o">),</span> <span class="o">%</span><span class="nv">rdx</span>
  <span class="nb">movq</span>  <span class="nv">__NSConcreteStackBlock</span><span class="no">@</span><span class="nv">GOTPCREL</span><span class="o">(%</span><span class="nv">rip</span><span class="o">),</span> <span class="o">%</span><span class="nv">rsi</span>
  <span class="nb">movq</span>  <span class="o">%</span><span class="nv">rsi</span><span class="o">,</span> <span class="o">-</span><span class="mi">72</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>                              <span class="o">##</span> <span class="err">初始化</span> <span class="nv">isa</span>
  <span class="nb">movl</span>  <span class="err">$</span><span class="o">-</span><span class="mi">1073741824</span><span class="o">,</span> <span class="o">-</span><span class="mi">64</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span> <span class="o">##</span> <span class="nv">imm</span> <span class="o">=</span> <span class="mh">0xC0000000</span>  <span class="o">##</span> <span class="err">初始化</span> <span class="nv">flags</span>
  <span class="nb">movl</span>  <span class="err">$</span><span class="mi">0</span><span class="o">,</span> <span class="o">-</span><span class="mi">60</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>                   <span class="o">##</span> <span class="err">初始化</span> <span class="nv">reserved</span> 
  <span class="nb">movq</span>  <span class="o">%</span><span class="nv">rdx</span><span class="o">,</span> <span class="o">-</span><span class="mi">56</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>                 <span class="o">##</span> <span class="err">初始化</span> <span class="nv">invoke</span>
  <span class="nb">movq</span>  <span class="o">%</span><span class="nv">rcx</span><span class="o">,</span> <span class="o">-</span><span class="mi">48</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>                 <span class="o">##</span> <span class="err">初始化</span> <span class="nv">descriptor</span>
  <span class="nb">movl</span>  <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">),</span> <span class="o">%</span><span class="nv">edi</span>                 <span class="o">##</span> <span class="err">这两行把</span> <span class="nv">i</span> <span class="err">的值复制给</span> <span class="nv">block</span> <span class="err">里面的副本</span>
  <span class="nb">movl</span>  <span class="o">%</span><span class="nv">edi</span><span class="o">,</span> <span class="o">-</span><span class="mi">40</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span> 
  <span class="nb">movslq</span>  <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">),</span> <span class="o">%</span><span class="nv">rcx</span>               <span class="o">##</span> <span class="o">%</span><span class="nv">rcx</span> <span class="o">=</span> <span class="nv">i</span>
  <span class="nb">movq</span>  <span class="o">%</span><span class="nv">rax</span><span class="o">,</span> <span class="o">-</span><span class="mi">32</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">,%</span><span class="nv">rcx</span><span class="o">,</span><span class="mi">8</span><span class="o">)</span>          <span class="o">##</span> <span class="err">赋值给</span> <span class="nv">blocks</span><span class="o">[</span><span class="nv">i</span><span class="o">]</span>

  <span class="nb">movl</span>  <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">),</span> <span class="o">%</span><span class="nv">eax</span>                 <span class="o">##</span> <span class="o">++</span><span class="nv">i</span>
  <span class="nb">addl</span>  <span class="err">$</span><span class="mi">1</span><span class="o">,</span> <span class="o">%</span><span class="nv">eax</span>
  <span class="nb">movl</span>  <span class="o">%</span><span class="nv">eax</span><span class="o">,</span> <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>
  <span class="nb">jmp</span> <span class="nv">LBB0_1</span>
<span class="nl">LBB0_4</span><span class="err">:</span>                                 <span class="err">#</span><span class="k">#</span> <span class="err">第二个循环</span>
  <span class="nb">movl</span>  <span class="err">$</span><span class="mi">0</span><span class="o">,</span> <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>
<span class="err">...</span>
</code></pre></div></div>

<p>第一个循环结束之后栈内存大概长这个样子：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>%rsp       -&gt; |-------------------------------------------|
                                 ...
              |-------------------------------------------|
              |       block[2] = %rax = -72(%rbp)         |-----------------|
              |                                           |                 |
-16(%rsp)  -&gt; |-------------------------------------------|                 |
              |       block[1] = %rax = -72(%rbp)         |-----------------|
              |                                           |                 |
-24(%rsp)  -&gt; |-------------------------------------------|                 |
              |       block[0] = %rax = -72(%rbp)         |-----------------|
              |                                           |                 |
-32(%rsp)  -&gt; |-------------------------------------------|                 |
              |       2 // local int i                    |                 |
-36(%rsp)  -&gt; |-------------------------------------------| ----            |
              |       2 // block const int i              |    |            |
              |-------------------------------------------|    |            |
              |       &amp;___block_descriptor_tmp            |    |            |
              |                                           |    |            |
              |-------------------------------------------|    |            |
              |       &amp;___main_block_invoke               |    |            |
              |                                           |    |            |
              |-------------------------------------------| block_literal   |
              |       0 // reserved                       |    |            |
              |-------------------------------------------|    |            |
              |       0xC0000000 // flags                 |    |            |
              |-------------------------------------------|    |            |
              |       &amp;__NSConcreteStackBlock             |    |            |
              |                                           |    |            |
-72(%rsp)  -&gt; |-------------------------------------------| ----     &lt;&lt;&lt;----|
                                 ...
</code></pre></div></div>

<p>看汇编代码就很清晰了，基本上 block 这块内存来回被初始化了 3 次，之后 block 里面的副本 i 变成了 2。</p>

<h2 id="arc-模式下的-block">ARC 模式下的 block</h2>

<p>我们继续看 ARC 模式下的 block，我们先拿到汇编代码：</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clang <span class="nt">-fno-stack-protector</span> <span class="nt">-fobjc-arc</span> <span class="nt">-S</span> blocktest.m <span class="nt">-o</span> blocktest_arc.s
</code></pre></div></div>

<p>同样摘取第一个循环部分，重点看两边不同的地方：</p>

<div class="language-armasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">...</span>
<span class="nl">_main</span><span class="err">:</span>
  <span class="nb">pushq</span> <span class="o">%</span><span class="nv">rbp</span>
  <span class="nb">movq</span>  <span class="o">%</span><span class="nv">rsp</span><span class="o">,</span> <span class="o">%</span><span class="nv">rbp</span>

<span class="err">##</span> <span class="err">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span>
  <span class="err">#</span><span class="k">#</span> <span class="err">这里不同的地方在于</span> <span class="nv">ARC</span> <span class="err">模式下会用</span> <span class="nv">memset</span> <span class="err">清空</span> <span class="nv">blocks</span> 
  <span class="nb">subq</span>  <span class="err">$</span><span class="mi">112</span><span class="o">,</span> <span class="o">%</span><span class="nv">rsp</span>
  <span class="nb">xorl</span>  <span class="o">%</span><span class="nv">esi</span><span class="o">,</span> <span class="o">%</span><span class="nv">esi</span>
  <span class="nb">movl</span>  <span class="err">$</span><span class="mi">24</span><span class="o">,</span> <span class="o">%</span><span class="nv">eax</span>
  <span class="nb">movl</span>  <span class="o">%</span><span class="nv">eax</span><span class="o">,</span> <span class="o">%</span><span class="nv">edx</span>
  <span class="nb">leaq</span>  <span class="o">-</span><span class="mi">32</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">),</span> <span class="o">%</span><span class="nv">rcx</span>
  <span class="nb">movl</span>  <span class="err">$</span><span class="mi">0</span><span class="o">,</span> <span class="o">-</span><span class="mi">4</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>
  <span class="nb">movq</span>  <span class="o">%</span><span class="nv">rcx</span><span class="o">,</span> <span class="o">%</span><span class="nv">rdi</span>
  <span class="nb">callq</span> <span class="nv">_memset</span>
<span class="err">##</span> <span class="err">&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;</span>

  <span class="nb">movl</span>  <span class="err">$</span><span class="mi">0</span><span class="o">,</span> <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>                   <span class="o">##</span> <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span> <span class="err">存的是局部变量</span> <span class="nv">i</span>
<span class="nl">LBB0_1</span><span class="err">:</span>                                 <span class="err">#</span><span class="k">#</span> <span class="err">第一个循环</span>
  <span class="nb">cmpl</span>  <span class="err">$</span><span class="mi">3</span><span class="o">,</span> <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>
  <span class="nb">jge</span> <span class="nv">LBB0_4</span>                            <span class="o">##</span> <span class="nv">i</span> <span class="o">&gt;=</span> <span class="mi">3</span> <span class="err">则跳出循环</span>

  <span class="nb">leaq</span>  <span class="o">-</span><span class="mi">72</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">),</span> <span class="o">%</span><span class="nv">rax</span>                 <span class="o">##</span> <span class="nv">block</span> <span class="err">的地址</span>
  <span class="nb">leaq</span>  <span class="nv">___block_descriptor_tmp</span><span class="o">(%</span><span class="nv">rip</span><span class="o">),</span> <span class="o">%</span><span class="nv">rcx</span>
  <span class="nb">leaq</span>  <span class="nv">___main_block_invoke</span><span class="o">(%</span><span class="nv">rip</span><span class="o">),</span> <span class="o">%</span><span class="nv">rdx</span>
  <span class="nb">movq</span>  <span class="nv">__NSConcreteStackBlock</span><span class="no">@</span><span class="nv">GOTPCREL</span><span class="o">(%</span><span class="nv">rip</span><span class="o">),</span> <span class="o">%</span><span class="nv">rsi</span>
  <span class="nb">movq</span>  <span class="o">%</span><span class="nv">rsi</span><span class="o">,</span> <span class="o">-</span><span class="mi">72</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>                              <span class="o">##</span> <span class="err">初始化</span> <span class="nv">isa</span>
  <span class="nb">movl</span>  <span class="err">$</span><span class="o">-</span><span class="mi">1073741824</span><span class="o">,</span> <span class="o">-</span><span class="mi">64</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span> <span class="o">##</span> <span class="nv">imm</span> <span class="o">=</span> <span class="mh">0xC0000000</span>  <span class="o">##</span> <span class="err">初始化</span> <span class="nv">flags</span>
  <span class="nb">movl</span>  <span class="err">$</span><span class="mi">0</span><span class="o">,</span> <span class="o">-</span><span class="mi">60</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>                   <span class="o">##</span> <span class="err">初始化</span> <span class="nv">reserved</span> 
  <span class="nb">movq</span>  <span class="o">%</span><span class="nv">rdx</span><span class="o">,</span> <span class="o">-</span><span class="mi">56</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>                 <span class="o">##</span> <span class="err">初始化</span> <span class="nv">invoke</span>
  <span class="nb">movq</span>  <span class="o">%</span><span class="nv">rcx</span><span class="o">,</span> <span class="o">-</span><span class="mi">48</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>                 <span class="o">##</span> <span class="err">初始化</span> <span class="nv">descriptor</span>
  <span class="nb">movl</span>  <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">),</span> <span class="o">%</span><span class="nv">edi</span>                 <span class="o">##</span> <span class="err">这两行把</span> <span class="nv">i</span> <span class="err">的值复制给</span> <span class="nv">block</span> <span class="err">里面的副本</span>

<span class="err">##</span> <span class="err">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;</span>
  <span class="err">#</span><span class="k">#</span> <span class="err">这里不同的地方是我们初始化</span> <span class="nv">block</span> <span class="err">之后，会调用</span> <span class="nv">_bojc_retainBlock</span> <span class="err">把</span> <span class="nv">block</span> <span class="err">复制到</span> <span class="nv">heap</span> <span class="err">离去</span>
  <span class="err">#</span><span class="k">#</span> <span class="nv">blocks</span><span class="o">[</span><span class="nv">i</span><span class="o">]</span> <span class="err">保存的</span> <span class="nv">heap</span> <span class="err">里面的</span> <span class="nv">block</span>
  <span class="nb">movq</span>  <span class="o">%</span><span class="nv">rax</span><span class="o">,</span> <span class="o">%</span><span class="nv">rdi</span>                      <span class="o">##</span> <span class="nv">_objc_retainBlock</span><span class="o">(</span><span class="nv">__block_literal</span><span class="o">)</span>
  <span class="nb">callq</span> <span class="nv">_objc_retainBlock</span>
  <span class="nb">movslq</span>  <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">),</span> <span class="o">%</span><span class="nv">rcx</span>
  <span class="nb">movq</span>  <span class="o">-</span><span class="mi">32</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">,%</span><span class="nv">rcx</span><span class="o">,</span><span class="mi">8</span><span class="o">),</span> <span class="o">%</span><span class="nv">rdx</span>          <span class="o">##</span> <span class="o">%</span><span class="nv">rdx</span> <span class="o">=</span> <span class="nv">blocks</span><span class="o">[</span><span class="nv">i</span><span class="o">]</span>
  <span class="nb">movq</span>  <span class="o">%</span><span class="nv">rax</span><span class="o">,</span> <span class="o">-</span><span class="mi">32</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">,%</span><span class="nv">rcx</span><span class="o">,</span><span class="mi">8</span><span class="o">)</span>          <span class="o">##</span> <span class="nv">blocks</span><span class="o">[</span><span class="nv">i</span><span class="o">]</span> <span class="o">=</span> <span class="nv">_objc_retainBlock</span><span class="o">(</span><span class="nv">__block_literal</span><span class="o">)</span>
  <span class="nb">movq</span>  <span class="o">%</span><span class="nv">rdx</span><span class="o">,</span> <span class="o">%</span><span class="nv">rdi</span>                      <span class="o">##</span> <span class="nv">_objc_release</span><span class="o">(%</span><span class="nv">rdx</span><span class="o">)</span>
  <span class="nb">callq</span> <span class="nv">_objc_release</span>
<span class="err">##</span> <span class="err">&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;</span>

  <span class="nb">movl</span>  <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">),</span> <span class="o">%</span><span class="nv">eax</span>                 <span class="o">##</span> <span class="o">++</span><span class="nv">i</span>
  <span class="nb">addl</span>  <span class="err">$</span><span class="mi">1</span><span class="o">,</span> <span class="o">%</span><span class="nv">eax</span>
  <span class="nb">movl</span>  <span class="o">%</span><span class="nv">eax</span><span class="o">,</span> <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>
  <span class="nb">jmp</span> <span class="nv">LBB0_1</span>
<span class="nl">LBB0_4</span><span class="err">:</span>                                 <span class="err">#</span><span class="k">#</span> <span class="err">第二个循环</span>
  <span class="nb">movl</span>  <span class="err">$</span><span class="mi">0</span><span class="o">,</span> <span class="o">-</span><span class="mi">36</span><span class="o">(%</span><span class="nv">rbp</span><span class="o">)</span>
<span class="err">...</span>
</code></pre></div></div>

<p>到这里这个问题就算破案了，欲知后事如何，请听下回分解！</p>]]></content><author><name>Guangming Mao</name></author><category term="blog" /><category term="Assembly" /><category term="Objective-C" /><summary type="html"><![CDATA[搬运自 https://zhuanlan.zhihu.com/p/40663395]]></summary></entry><entry><title type="html">Colorize Language in VSCode</title><link href="https://maoguangming.com/blog/colorize-language-in-vscode.html" rel="alternate" type="text/html" title="Colorize Language in VSCode" /><published>2016-05-22T22:56:00-07:00</published><updated>2016-05-22T22:56:00-07:00</updated><id>https://maoguangming.com/blog/colorize-language-in-vscode</id><content type="html" xml:base="https://maoguangming.com/blog/colorize-language-in-vscode.html"><![CDATA[<blockquote>
  <p>微软大法好</p>
</blockquote>

<p>VSCode 是个好编辑器，虽然插件挫了点，但是跑得比 Atom 快，调试功能特别赞，对 JavaScript 和
Go 的代码提示简直不能再好，将来插件生态再好一点，这就是一个全能的 IDE 了。</p>

<p>VSCode 在很早就开放了<a href="https://code.visualstudio.com/Docs/extensions/overview">插件系统</a>，
这里主要介绍一下语法高亮插件的部分。</p>

<p>VSCode 的语法高亮配置和 Atom 一样，使用的是 textmate
的<a href="https://manual.macromates.com/en/language_grammars">配置语法</a>。</p>

<h2 id="例子">例子</h2>

<p>来看一个例子：</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"scopeName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"source.untitled"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"fileTypes"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
    </span><span class="nl">"foldingStartMarker"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\{\s</span><span class="s2">*$"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"foldingStopMarker"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^</span><span class="se">\s</span><span class="s2">*</span><span class="se">\}</span><span class="s2">'"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"keyword.control.untitled"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"match"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\b</span><span class="s2">(if|while|for|return)</span><span class="se">\b</span><span class="s2">"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string.quoted.double.untitled"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"begin"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\"</span><span class="s2">"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\"</span><span class="s2">"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
                </span><span class="p">{</span><span class="w">
                    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"constant.character.escape.untitled"</span><span class="p">,</span><span class="w">
                    </span><span class="nl">"match"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\\</span><span class="s2">."</span><span class="w">
                </span><span class="p">}</span><span class="w">
            </span><span class="p">]</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>主要配置有几部分：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">scopeName</code>: 这个是用来标识语言的，必须独一无二，一般使用 <code class="language-plaintext highlighter-rouge">.</code> 分割成两个部分，
前半部分一般是 <code class="language-plaintext highlighter-rouge">text</code> / <code class="language-plaintext highlighter-rouge">source</code>，后面是具体的语言名字</li>
  <li><code class="language-plaintext highlighter-rouge">fileTypes</code>: 这个用来指定配置适用的文件扩展名</li>
  <li><code class="language-plaintext highlighter-rouge">foldingStartMarker</code> / <code class="language-plaintext highlighter-rouge">foldingStopMarker</code>: 用来控制折叠</li>
  <li><code class="language-plaintext highlighter-rouge">patterns</code>: 这才是真正用来识别高亮的规则</li>
</ul>

<p>另外还有两个不在例子里面的配置：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">firstLineMatch</code>: 如果匹配了文档第一行，这个文档就适用这个高亮规则，比如 <code class="language-plaintext highlighter-rouge">^#!/.*\bruby\b</code></li>
  <li><code class="language-plaintext highlighter-rouge">repository</code>: 这是一堆键值对形式的规则，可以在文档中使用 <code class="language-plaintext highlighter-rouge">include</code> 引用这些规则</li>
</ul>

<h2 id="规则">规则</h2>

<p>规则使用键值对形式，键可以是：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">name</code>: 指定规则的名字，是用来高亮的依据，需要遵循一定的命名规则来让编辑器知道该高亮成什么类型</li>
  <li><code class="language-plaintext highlighter-rouge">match</code>: 匹配文本的正则表达式</li>
  <li><code class="language-plaintext highlighter-rouge">begin</code>, <code class="language-plaintext highlighter-rouge">end</code>: 匹配多行文本的规则，和 <code class="language-plaintext highlighter-rouge">match</code> 不能共存，<code class="language-plaintext highlighter-rouge">begin</code> 是匹配开头的正则，
<code class="language-plaintext highlighter-rouge">end</code> 是匹配结尾的正则，<code class="language-plaintext highlighter-rouge">begin</code> 里面 capture 到内容可以在 <code class="language-plaintext highlighter-rouge">end</code> 里面引用，比如匹配
heredoc 的时候：
    <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">  </span><span class="p">{</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string.unquoted.here-doc"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"begin"</span><span class="p">:</span><span class="w"> </span><span class="s2">"&lt;&lt;(</span><span class="se">\w</span><span class="s2">+)"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^</span><span class="se">\1</span><span class="s2">$"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span></code></pre></div>    </div>
  </li>
  <li><code class="language-plaintext highlighter-rouge">contentName</code>: 类似 <code class="language-plaintext highlighter-rouge">name</code>，但是是设置 <code class="language-plaintext highlighter-rouge">begin</code> 和 <code class="language-plaintext highlighter-rouge">end</code> 中间文本的类型的</li>
  <li><code class="language-plaintext highlighter-rouge">captures</code>, <code class="language-plaintext highlighter-rouge">beginCaptures</code>, <code class="language-plaintext highlighter-rouge">endCaptures</code>: 设置正则匹配到的部分的类型，用来高亮，只能包含 <code class="language-plaintext highlighter-rouge">name</code></li>
  <li><code class="language-plaintext highlighter-rouge">include</code>: 引用其他语言的高亮规则或者引用 <code class="language-plaintext highlighter-rouge">repository</code> 里面的东西</li>
</ul>

<h2 id="命名规则">命名规则</h2>

<p>为了让主题文件可以通用，<code class="language-plaintext highlighter-rouge">name</code> 设置要遵循一定的规则，这样编辑器就知道这块地方是注释，那块是关键字，
从而为不同的部分设置不同的颜色，高亮就是这么来的。名字各部分之间使用 <code class="language-plaintext highlighter-rouge">.</code> 连接起来，比如
<code class="language-plaintext highlighter-rouge">comment.line.number-sign</code>：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">comment</code> 设置注释
    <ul>
      <li><code class="language-plaintext highlighter-rouge">line</code> 单行注释，一般使用的有
        <ul>
          <li><code class="language-plaintext highlighter-rouge">double-slash</code> //</li>
          <li><code class="language-plaintext highlighter-rouge">double-dash</code> –</li>
          <li><code class="language-plaintext highlighter-rouge">number-sign</code> #</li>
          <li><code class="language-plaintext highlighter-rouge">percentage</code> — %</li>
          <li><em>character</em> 其他类型的单行注释</li>
        </ul>
      </li>
      <li><code class="language-plaintext highlighter-rouge">block</code> 多行注释，比如 <code class="language-plaintext highlighter-rouge">/* … */</code>，<code class="language-plaintext highlighter-rouge">&lt;!-- … --&gt;</code>
        <ul>
          <li><code class="language-plaintext highlighter-rouge">documentation</code></li>
        </ul>
      </li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">constant</code> 常量
    <ul>
      <li><code class="language-plaintext highlighter-rouge">numeric</code> 数字</li>
      <li><code class="language-plaintext highlighter-rouge">character</code> 字符，比如<code class="language-plaintext highlighter-rouge">&amp;lt;</code>，<code class="language-plaintext highlighter-rouge">\e</code>，<code class="language-plaintext highlighter-rouge">\031</code>
        <ul>
          <li><code class="language-plaintext highlighter-rouge">escape</code> 转义字符</li>
        </ul>
      </li>
      <li><code class="language-plaintext highlighter-rouge">language</code> 语言支持的常量，<code class="language-plaintext highlighter-rouge">true</code> / <code class="language-plaintext highlighter-rouge">false</code> / <code class="language-plaintext highlighter-rouge">nil</code> 等等</li>
      <li><code class="language-plaintext highlighter-rouge">other</code> 比如 css 里面的颜色</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">entity</code> 各种实体，类名，方法名等
    <ul>
      <li><code class="language-plaintext highlighter-rouge">name</code>
        <ul>
          <li><code class="language-plaintext highlighter-rouge">function</code> 方法名</li>
          <li><code class="language-plaintext highlighter-rouge">type</code> 类型名</li>
          <li><code class="language-plaintext highlighter-rouge">tag</code> 标签名</li>
          <li><code class="language-plaintext highlighter-rouge">section</code> 区块名</li>
        </ul>
      </li>
      <li><code class="language-plaintext highlighter-rouge">other</code> 其他实体
        <ul>
          <li><code class="language-plaintext highlighter-rouge">inherited-class</code> 继承的类名</li>
          <li><code class="language-plaintext highlighter-rouge">attribute-name</code> 属性名</li>
        </ul>
      </li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">invalid</code> 表示非法内容
    <ul>
      <li><code class="language-plaintext highlighter-rouge">illegal</code></li>
      <li><code class="language-plaintext highlighter-rouge">deprecated</code></li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">keyword</code> 关键字
    <ul>
      <li><code class="language-plaintext highlighter-rouge">control</code> 流程控制类 <code class="language-plaintext highlighter-rouge">if</code> / <code class="language-plaintext highlighter-rouge">while</code> 之类</li>
      <li><code class="language-plaintext highlighter-rouge">operator</code> 运算符，比如 <code class="language-plaintext highlighter-rouge">or</code></li>
      <li><code class="language-plaintext highlighter-rouge">other</code> 其他</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">markup</code> 为标记语言使用的，顾名思义即可
    <ul>
      <li><code class="language-plaintext highlighter-rouge">underline</code></li>
      <li><code class="language-plaintext highlighter-rouge">bold</code></li>
      <li><code class="language-plaintext highlighter-rouge">heading</code></li>
      <li><code class="language-plaintext highlighter-rouge">italic</code></li>
      <li><code class="language-plaintext highlighter-rouge">quote</code></li>
      <li><code class="language-plaintext highlighter-rouge">raw</code> 比如代码块，无格式</li>
      <li><code class="language-plaintext highlighter-rouge">other</code></li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">meta</code> 表示不同的部分，比如 <code class="language-plaintext highlighter-rouge">meta.class</code>，<code class="language-plaintext highlighter-rouge">meta.method</code> 目的是更好的组织这些规则</li>
  <li><code class="language-plaintext highlighter-rouge">storage</code> 与数据存储相关的
    <ul>
      <li><code class="language-plaintext highlighter-rouge">type</code> 类型 <code class="language-plaintext highlighter-rouge">class</code> / <code class="language-plaintext highlighter-rouge">function</code> / <code class="language-plaintext highlighter-rouge">int</code> / <code class="language-plaintext highlighter-rouge">var</code> 等</li>
      <li><code class="language-plaintext highlighter-rouge">modifier</code> 修饰符 <code class="language-plaintext highlighter-rouge">static</code> / <code class="language-plaintext highlighter-rouge">const</code> / <code class="language-plaintext highlighter-rouge">final</code> 啥的</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">string</code> 字符串
    <ul>
      <li><code class="language-plaintext highlighter-rouge">quoted</code>
        <ul>
          <li><code class="language-plaintext highlighter-rouge">single</code> 单引号引用的</li>
          <li><code class="language-plaintext highlighter-rouge">double</code> 双引号引用的</li>
          <li><code class="language-plaintext highlighter-rouge">triple</code> 三个引号引用的 比如 <code class="language-plaintext highlighter-rouge">"""Python"""</code></li>
          <li><code class="language-plaintext highlighter-rouge">other</code> 另类的，比如 <code class="language-plaintext highlighter-rouge">$'shell'</code>，<code class="language-plaintext highlighter-rouge">%{ruby}</code></li>
        </ul>
      </li>
      <li><code class="language-plaintext highlighter-rouge">unquoted</code> here-doc
        <ul>
          <li><code class="language-plaintext highlighter-rouge">interpolated</code> 要<strong>解析</strong>的字符串：<code class="language-plaintext highlighter-rouge">`date`</code>, <code class="language-plaintext highlighter-rouge">$(pwd)</code></li>
          <li><code class="language-plaintext highlighter-rouge">regexp</code> 正则表达式</li>
          <li><code class="language-plaintext highlighter-rouge">other</code> 其他类型的字符串</li>
        </ul>
      </li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">support</code> 框架或者库提供的功能
    <ul>
      <li><code class="language-plaintext highlighter-rouge">function</code> 比如 <code class="language-plaintext highlighter-rouge">C</code> 里面的 <code class="language-plaintext highlighter-rouge">printf</code></li>
      <li><code class="language-plaintext highlighter-rouge">class</code> 比如 <code class="language-plaintext highlighter-rouge">C++</code> 里面的 <code class="language-plaintext highlighter-rouge">vector</code></li>
      <li><code class="language-plaintext highlighter-rouge">type</code></li>
      <li><code class="language-plaintext highlighter-rouge">constant</code></li>
      <li><code class="language-plaintext highlighter-rouge">variable</code></li>
      <li><code class="language-plaintext highlighter-rouge">other</code></li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">variable</code>
    <ul>
      <li><code class="language-plaintext highlighter-rouge">parameter</code> 函数参数</li>
      <li><code class="language-plaintext highlighter-rouge">language</code> 语言预定于的变量，比如 <code class="language-plaintext highlighter-rouge">this</code> / <code class="language-plaintext highlighter-rouge">super</code> / <code class="language-plaintext highlighter-rouge">self</code></li>
      <li><code class="language-plaintext highlighter-rouge">other</code></li>
    </ul>
  </li>
</ul>

<h2 id="实战">实战</h2>

<p>有了这些东西就可以开始为一个语言写高亮配置文件了，这里选用一个简单的语言 Cool，
Cool (Classroom Object Oriented Language) 是 Stanford 编译器课程的教学语言，语法比较简单</p>

<p>具体语法可以查看 <a href="http://theory.stanford.edu/~aiken/software/cool/cool.html">http://theory.stanford.edu/~aiken/software/cool/cool.html</a></p>

<p>具体例子可参考 <a href="https://github.com/maogm12/language-cool">https://github.com/maogm12/language-cool</a></p>

<p>在 VSCode 中安装的命令 <code class="language-plaintext highlighter-rouge">ext install cool</code></p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"scopeName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"source.cool"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"COOL"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"fileTypes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"cl"</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#code"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"repository"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"block"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"begin"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="s2">"}"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"meta.block.cool"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
            </span><span class="p">{</span><span class="w">
              </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#code"</span><span class="w">
            </span><span class="p">}</span><span class="w">
          </span><span class="p">]</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"builtins"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"match"</span><span class="p">:</span><span class="w"> </span><span class="s2">"(Int|String|Bool|IO|Object)"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"support.class.cool"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"match"</span><span class="p">:</span><span class="w"> </span><span class="s2">"(abort|type_name|copy|out_string|out_int|in_string|in_int|length|concat|substr)"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"support.function.cool"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"match"</span><span class="p">:</span><span class="w"> </span><span class="s2">"(self|SELF_TYPE)"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"variable.language.cool"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"class"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"begin"</span><span class="p">:</span><span class="w"> </span><span class="s2">"((?i:class))</span><span class="se">\\</span><span class="s2">s+([A-Z][A-Za-z0-9_]*)(</span><span class="se">\\</span><span class="s2">s+((?i:inherits))</span><span class="se">\\</span><span class="s2">s+([A-Z][A-Za-z0-9_]*))?</span><span class="se">\\</span><span class="s2">b"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"beginCaptures"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"1"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"storage.type.cool"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"2"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"entity.name.type.class.cool"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"4"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"keyword.operator.cool"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"5"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"entity.name.type.class.cool"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="s2">"}"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"meta.class.cool"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"begin"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="s2">"(?=})"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"meta.class.body.cs"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
            </span><span class="p">{</span><span class="w">
              </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#method"</span><span class="w">
            </span><span class="p">},</span><span class="w">
            </span><span class="p">{</span><span class="w">
              </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#code"</span><span class="w">
            </span><span class="p">}</span><span class="w">
          </span><span class="p">]</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"code"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#block"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#builtins"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#class"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#comments"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#constants"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#keywords"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#method"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"line_comment"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"begin"</span><span class="p">:</span><span class="w"> </span><span class="s2">"--"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="s2">"$</span><span class="se">\\</span><span class="s2">n?"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"comment.line.double-dash.cool"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"block_comment"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"begin"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\\</span><span class="s2">(</span><span class="se">\\</span><span class="s2">*"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\\</span><span class="s2">*</span><span class="se">\\</span><span class="s2">)</span><span class="se">\\</span><span class="s2">n?"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"comment.block.documentation.cool"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#block_comment"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"comments"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#line_comment"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#block_comment"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"constants"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"match"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\\</span><span class="s2">b(true|false)</span><span class="se">\\</span><span class="s2">b"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"constant.language.cool"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"match"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\\</span><span class="s2">b([1-9][0-9]*)</span><span class="se">\\</span><span class="s2">b"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"constant.numeric.cool"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"begin"</span><span class="p">:</span><span class="w"> </span><span class="s2">"(?&lt;!</span><span class="se">\\\\</span><span class="s2">)</span><span class="se">\"</span><span class="s2">"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="s2">"(?&lt;!</span><span class="se">\\\\</span><span class="s2">)</span><span class="se">\"</span><span class="s2">"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string.quoted.double.cool"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
            </span><span class="p">{</span><span class="w">
              </span><span class="nl">"match"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\\\\</span><span class="s2">(n|t)"</span><span class="p">,</span><span class="w">
              </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"constant.character.escape.cool"</span><span class="w">
            </span><span class="p">}</span><span class="w">
          </span><span class="p">]</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"keywords"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"match"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\\</span><span class="s2">b(if|then|else|fi|while|loop|pool|case|esac)</span><span class="se">\\</span><span class="s2">b"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"keyword.control.cool"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"match"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\\</span><span class="s2">b(in|inherits|isvoid|let|new|of|new|not)</span><span class="se">\\</span><span class="s2">b"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"keyword.operator.cool"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"match"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\\</span><span class="s2">b([a-z][A-Za-z0-9_]*)</span><span class="se">\\</span><span class="s2">s*</span><span class="se">\\</span><span class="s2">(</span><span class="se">\\</span><span class="s2">s*(?:([a-z][A-Za-z0-9_]*)</span><span class="se">\\</span><span class="s2">s*:</span><span class="se">\\</span><span class="s2">s*([A-Z][A-Za-z0-9_]*))?(?:</span><span class="se">\\</span><span class="s2">s*,</span><span class="se">\\</span><span class="s2">s*([a-z][A-Za-z0-9_]*)</span><span class="se">\\</span><span class="s2">s*:</span><span class="se">\\</span><span class="s2">s*([A-Z][A-Za-z0-9_]*))*</span><span class="se">\\</span><span class="s2">s*</span><span class="se">\\</span><span class="s2">)</span><span class="se">\\</span><span class="s2">s*:</span><span class="se">\\</span><span class="s2">s*([A-Z][A-Za-z0-9_]*)</span><span class="se">\\</span><span class="s2">b"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"meta.method.cool"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"captures"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"1"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
              </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"entity.name.function.cool"</span><span class="w">
            </span><span class="p">},</span><span class="w">
            </span><span class="nl">"2"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
              </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"variable.parameter.cool"</span><span class="w">
            </span><span class="p">},</span><span class="w">
            </span><span class="nl">"3"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
              </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"storage.type.cool"</span><span class="w">
            </span><span class="p">},</span><span class="w">
            </span><span class="nl">"4"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
              </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"variable.parameter.cool"</span><span class="w">
            </span><span class="p">},</span><span class="w">
            </span><span class="nl">"5"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
              </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"storage.type.cool"</span><span class="w">
            </span><span class="p">},</span><span class="w">
            </span><span class="nl">"6"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
              </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"storage.type.cool"</span><span class="w">
            </span><span class="p">}</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>效果如下：</p>

<p><img src="https://raw.githubusercontent.com/maogm12/language-cool/master/screenshot.png" alt="ScreenShot" /></p>]]></content><author><name>Guangming Mao</name></author><category term="blog" /><category term="VSCode" /><summary type="html"><![CDATA[微软大法好]]></summary></entry><entry><title type="html">An Atom Package Demo</title><link href="https://maoguangming.com/blog/atom-package-demo.html" rel="alternate" type="text/html" title="An Atom Package Demo" /><published>2016-05-08T23:09:00-07:00</published><updated>2016-05-08T23:09:00-07:00</updated><id>https://maoguangming.com/blog/atom-package-demo</id><content type="html" xml:base="https://maoguangming.com/blog/atom-package-demo.html"><![CDATA[<p>Atom 是 GitHub 出品的一个文本编辑器，基于 Electron，是一个基于 NodeJs 的项目。开源免费，
这也是我最初选用他的原因，还用 Electron 做过一段时间开发。</p>

<p>整个界面看起来很像 Sublime Text，是一个很良心的编辑器，唯一的缺点就是有点慢。</p>

<p>编辑器的灵魂在于插件，Sublime Text 这么受欢迎与那一大堆优秀的插件们是离不开，
VSCode 要加油啊。</p>

<p>最近写 Ruby 的项目，由于 RubyMine 太过于臃肿，转而还用了 Atom 做主力编辑器。
Atom 配置完成之后，写起代码来体验还是很棒的，现在比以前的版本也快多了。</p>

<p>作为工具党，插件我没少装，于是时时冒出自己学一下写插件的想法。Atom 底层是 NodeJs，
我也还能写一写。</p>

<p>正好在写单元测试的时候，测试大整数时，Rubocop 提示我 Ruby 中大整数应该使用 <code class="language-plaintext highlighter-rouge">_</code> 隔开，
类似 <code class="language-plaintext highlighter-rouge">123_456_789</code>，这样提高代码可读性。</p>

<p>那就写个轮子吧！！！</p>

<p>Atom 文档中有插件开发指南:
<a href="http://flight-manual.atom.io/hacking-atom/sections/package-modifying-text/">http://flight-manual.atom.io/hacking-atom/sections/package-modifying-text/</a></p>

<p>我的插件功能就是获取当前光标下的数字，然后把他们替换成下划线分割的格式。</p>

<p>使用 Atom 自带的包生成工具，可以很方便的生成插件的开发包，具体请参考官方文档。
由于不涉及到 View 的操作，我们的插件很简单，生成完之后，删除掉 View 操作的代码后，结构如下：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── lib
│   └── seperate-number.js
├── package.json
</code></pre></div></div>

<p>测试代码容我后续再补哈。。。</p>

<p>库里面关键是操作数字，实现两个功能：</p>

<ol>
  <li>添加分隔符</li>
  <li>取消分隔符</li>
</ol>

<p>看代码，添加主要是三个一分三个一分，取消分隔符更简单，将分隔符都替换成空即可：</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">NumberSeperator</span> <span class="p">{</span>
    <span class="kd">constructor</span><span class="p">(</span><span class="nx">seperator</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">seperator</span> <span class="o">=</span> <span class="nx">seperator</span> <span class="o">||</span> <span class="dl">'</span><span class="s1">_</span><span class="dl">'</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="nx">toggleNumber</span><span class="p">(</span><span class="nx">number</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">pos</span> <span class="o">=</span> <span class="nx">number</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">seperator</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">pos</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// no seperator</span>
            <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">seperateNumber</span><span class="p">(</span><span class="nx">number</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">seperator</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">pos</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">pos</span> <span class="o">&lt;=</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
            <span class="kd">var</span> <span class="nx">newPos</span> <span class="o">=</span> <span class="nx">number</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">seperator</span><span class="p">,</span> <span class="nx">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
            <span class="k">while</span> <span class="p">(</span><span class="nx">newPos</span> <span class="o">==</span> <span class="nx">pos</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">seperator</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
                <span class="nx">pos</span> <span class="o">=</span> <span class="nx">newPos</span><span class="p">;</span>
                <span class="nx">newPos</span> <span class="o">=</span> <span class="nx">number</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">seperator</span><span class="p">,</span> <span class="nx">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">newPos</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="nx">pos</span> <span class="o">==</span> <span class="nx">number</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">3</span> <span class="o">-</span> <span class="k">this</span><span class="p">.</span><span class="nx">seperator</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">unSeperateNumber</span><span class="p">(</span><span class="nx">number</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">seperator</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="nx">number</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="nx">seperateNumber</span><span class="p">(</span><span class="nx">number</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">number</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
            <span class="kd">var</span> <span class="nx">length</span> <span class="o">=</span> <span class="nx">number</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">length</span> <span class="o">&lt;=</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">return</span> <span class="nx">number</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="kd">var</span> <span class="nx">start</span> <span class="o">=</span> <span class="nx">length</span> <span class="o">%</span> <span class="mi">3</span><span class="p">;</span>
            <span class="kd">var</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">start</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">?</span> <span class="nx">number</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">start</span><span class="p">)</span> <span class="p">:</span> <span class="dl">''</span><span class="p">;</span>
            <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="nx">start</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o">+=</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="nx">i</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
                    <span class="nx">result</span> <span class="o">+=</span> <span class="k">this</span><span class="p">.</span><span class="nx">seperator</span><span class="p">;</span>
                <span class="p">}</span>
                <span class="nx">result</span> <span class="o">+=</span> <span class="nx">number</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="nx">i</span> <span class="o">+</span> <span class="mi">3</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">return</span> <span class="nx">result</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="dl">''</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="nx">unSeperateNumber</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">number_with_seperator</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nx">number_with_seperator</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">seperator</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="dl">''</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>面向对象，搞个类先。</p>

<p><code class="language-plaintext highlighter-rouge">toggleNumber</code> 主要是判断数字的格式，如果是没有分隔符的，我们就加上，如果有分隔符，
并且是正确的使用分割符的，我们就把分隔符去掉。</p>

<p>具体到插件的生命周期，每一个插件在 Atom 启动的时候都会调用 <code class="language-plaintext highlighter-rouge">active</code> 方法，
我们在这个方法里面注册我们的命令 <code class="language-plaintext highlighter-rouge">seperate-number:toggle</code>。</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">activate</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">subscriptions</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">CompositeDisposable</span><span class="p">();</span>

    <span class="c1">// Register command that toggles this view</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">subscriptions</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="nx">atom</span><span class="p">.</span><span class="nx">commands</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">atom-workspace</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
        <span class="dl">'</span><span class="s1">seperate-number:toggle</span><span class="dl">'</span><span class="p">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="k">this</span><span class="p">.</span><span class="nx">toggle</span><span class="p">()</span>
    <span class="p">}));</span>
<span class="p">}</span>
</code></pre></div></div>

<p>然后在 <code class="language-plaintext highlighter-rouge">toggle</code> 方法里面主要是选中当前光标下的单词，然后选出其中的数字部分进行操作。</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">toggle</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">editor</span> <span class="o">=</span> <span class="nx">atom</span><span class="p">.</span><span class="nx">workspace</span><span class="p">.</span><span class="nx">getActiveTextEditor</span><span class="p">())</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">editor</span><span class="p">.</span><span class="nx">getSelectedText</span><span class="p">()</span> <span class="o">===</span> <span class="dl">''</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">editor</span><span class="p">.</span><span class="nx">selectWordsContainingCursors</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="kd">const</span> <span class="nx">worker</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">NumberSeperator</span><span class="p">();</span>
        <span class="kd">var</span> <span class="nx">word</span> <span class="o">=</span> <span class="nx">editor</span><span class="p">.</span><span class="nx">getSelectedText</span><span class="p">();</span>
        <span class="kd">var</span> <span class="nx">numbers</span> <span class="o">=</span> <span class="nx">word</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/</span><span class="se">\d[</span><span class="sr">_</span><span class="se">\d]</span><span class="sr">+/g</span><span class="p">);</span>
        <span class="kd">var</span> <span class="nx">newNumber</span><span class="p">;</span>
        <span class="nx">numbers</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">number</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">word</span> <span class="o">=</span> <span class="nx">word</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">number</span><span class="p">,</span> <span class="nx">worker</span><span class="p">.</span><span class="nx">toggleNumber</span><span class="p">(</span><span class="nx">number</span><span class="p">));</span>
        <span class="p">});</span>
        <span class="nx">editor</span><span class="p">.</span><span class="nx">insertText</span><span class="p">(</span><span class="nx">word</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>看看效果吧，基本满足需求哈！</p>

<p><img src="../images/atom-package-demo/toggle.gif" alt="效果图" /></p>

<p>项目在这里：<a href="https://github.com/maogm12/separate-number">https://github.com/maogm12/separate-number</a></p>

<p>TODO：</p>

<ul>
  <li>Atom 多选的时候有问题</li>
  <li>扔 Atom 的插件库里面去</li>
</ul>]]></content><author><name>Guangming Mao</name></author><category term="blog" /><category term="Atom" /><category term="Javascript" /><summary type="html"><![CDATA[Atom 是 GitHub 出品的一个文本编辑器，基于 Electron，是一个基于 NodeJs 的项目。开源免费， 这也是我最初选用他的原因，还用 Electron 做过一段时间开发。]]></summary></entry><entry><title type="html">Draw a Tree on Mac</title><link href="https://maoguangming.com/blog/draw-a-tree-on-mac.html" rel="alternate" type="text/html" title="Draw a Tree on Mac" /><published>2016-04-24T23:59:00-07:00</published><updated>2016-04-24T23:59:00-07:00</updated><id>https://maoguangming.com/blog/draw-a-tree-on-mac</id><content type="html" xml:base="https://maoguangming.com/blog/draw-a-tree-on-mac.html"><![CDATA[<p>Lately I’m on a project with tree relation between some models,
and those relations are store in the database, each row represents a
parent-child link.</p>

<p>It’s OK for the code to use but a little unintuitive for us human beings to
understand the tree, for simple ones we can draw that on paper checking row by row,
while for complicated trees, it will cause us ton of time to just visualize the
tree.</p>

<p>And why not make a tool for drawing that tree :D</p>

<h2 id="overall">Overall</h2>

<p>For decoupling, the tool is made up of 2 parts: the backend and the front-end.
Then the font-end can only do the rendering and is replaceable by any other font-end.</p>

<h2 id="backend">Backend</h2>

<p>I plan to not involve detailed logic of the backend and only define the interface
for the front-end to fetch data.</p>

<p>And a sample response from the backend is like:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"root"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"description"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"children"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"level1"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"description"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"children"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"level22222"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"This is level2"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"level222"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"This is level2"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"children"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
            </span><span class="p">{</span><span class="w">
              </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w">
              </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"level333"</span><span class="p">,</span><span class="w">
              </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Here are a few key resources to get you started with building mobile apps quickly"</span><span class="p">,</span><span class="w">
              </span><span class="nl">"children"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
                </span><span class="p">{</span><span class="w">
                  </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">11</span><span class="p">,</span><span class="w">
                  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"level444444"</span><span class="p">,</span><span class="w">
                  </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Introduces Android development. Covers the tool chain, Xamarin.Android projects, and Android fundamentals."</span><span class="w">
                </span><span class="p">},</span><span class="w">
                </span><span class="p">{</span><span class="w">
                  </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="p">,</span><span class="w">
                  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"level 4444"</span><span class="p">,</span><span class="w">
                  </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"This guide walks through the installation steps and configuration details required to install Xamarin.Android on Windows."</span><span class="w">
                </span><span class="p">}</span><span class="w">
              </span><span class="p">]</span><span class="w">
            </span><span class="p">}</span><span class="w">
          </span><span class="p">]</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"level1111"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"description"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"children"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"level2222222"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Never know this level: level2, 233333"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"level111111111"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"description"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"children"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"level22222222222"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"blahblahblah"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"level222"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"The AppDelegate.cs file contains our AppDelegate class"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">9</span><span class="p">,</span><span class="w">
          </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"level two"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"which is responsible for creating our window and listening to OS events"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>It’s JSON formatted with the <code class="language-plaintext highlighter-rouge">children</code> representing the relationship.</p>

<h1 id="front-end">Front-end</h1>

<p>This time I try to implement the front-end on Mac using Xamarin.Mac.</p>

<p><a href="https://www.xamarin.com/platform">Xamarin</a> is a nice tool for cross-platform development
using C#. And Xamarin.Mac is for developing Mac native app.</p>

<p>Our result is like:</p>

<p><img src="../images/draw-a-tree-on-mac/result.png" alt="result" /></p>

<p>Just create a new solution for our <code class="language-plaintext highlighter-rouge">TreeViewer</code>. The IDE - Xamarin Studio takes the
advantage of the Xcode to editing storyboard files.</p>

<p>We use a horizon <code class="language-plaintext highlighter-rouge">SplitView</code> for the main structure. The we draw a text field for
entering root ID, and a draw button the draw the tree.</p>

<p>We should custom a view to draw the actual tree. Let’s make it <code class="language-plaintext highlighter-rouge">TreeView</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">TreeView</code> is subclass of the <code class="language-plaintext highlighter-rouge">NSControl</code> on Cocoa framework.</p>

<p>The key point is calculating the tree size and positions of each nodes/links.</p>

<p>Here, we use <code class="language-plaintext highlighter-rouge">json.NET</code> to parsing the JSON response and using a generic object
to representing the tree, because the backend may response extra info in node,
so there is no a fixed node class.</p>

<p>The width／height calculation is as follows:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="kt">int</span> <span class="nf">calcTreeWidth</span><span class="p">(</span><span class="n">JObject</span> <span class="n">root</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">root</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="m">0</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// no children, the leaf node</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">root</span> <span class="p">[</span><span class="s">"children"</span><span class="p">]</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">root</span><span class="p">.</span><span class="nf">Add</span> <span class="p">(</span><span class="n">NODE_WIDTH_TAG</span><span class="p">,</span> <span class="n">NodeWidth</span><span class="p">);</span>
        <span class="k">return</span> <span class="n">NodeWidth</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">int</span> <span class="n">childrenWidth</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
    <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">child</span> <span class="k">in</span> <span class="n">root</span><span class="p">[</span><span class="s">"children"</span><span class="p">])</span> <span class="p">{</span>
        <span class="n">childrenWidth</span> <span class="p">+=</span> <span class="nf">calcTreeWidth</span> <span class="p">(</span><span class="n">child</span><span class="p">.</span><span class="n">Value</span><span class="p">&lt;</span><span class="n">JObject</span><span class="p">&gt;());</span>
    <span class="p">}</span>
    <span class="n">root</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">NODE_WIDTH_TAG</span><span class="p">,</span> <span class="n">Math</span><span class="p">.</span><span class="nf">Max</span> <span class="p">(</span><span class="n">childrenWidth</span><span class="p">,</span> <span class="n">NodeWidth</span><span class="p">));</span>
    <span class="k">return</span> <span class="n">Math</span><span class="p">.</span><span class="nf">Max</span> <span class="p">(</span><span class="n">childrenWidth</span><span class="p">,</span> <span class="n">NodeWidth</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">private</span> <span class="kt">int</span> <span class="nf">calcTreeHeight</span><span class="p">(</span><span class="n">JObject</span> <span class="n">root</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">root</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">return</span> <span class="m">0</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="c1">// leaf</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">root</span> <span class="p">[</span><span class="s">"children"</span><span class="p">]</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">root</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">NODE_HEIGHT_TAG</span><span class="p">,</span> <span class="n">NodeHeight</span><span class="p">);</span>
		<span class="k">return</span> <span class="n">NodeHeight</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="kt">int</span> <span class="n">childrenHeight</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
	<span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">child</span> <span class="k">in</span> <span class="n">root</span><span class="p">[</span><span class="s">"children"</span><span class="p">])</span> <span class="p">{</span>
		<span class="kt">int</span> <span class="n">height</span> <span class="p">=</span> <span class="nf">calcTreeHeight</span> <span class="p">(</span><span class="n">child</span><span class="p">.</span><span class="n">Value</span><span class="p">&lt;</span><span class="n">JObject</span><span class="p">&gt;());</span>
		<span class="n">childrenHeight</span> <span class="p">=</span> <span class="n">Math</span><span class="p">.</span><span class="nf">Max</span> <span class="p">(</span><span class="n">height</span><span class="p">,</span> <span class="n">childrenHeight</span><span class="p">);</span>
	<span class="p">}</span>
	<span class="n">root</span><span class="p">.</span><span class="nf">Add</span> <span class="p">(</span><span class="n">NODE_HEIGHT_TAG</span><span class="p">,</span> <span class="n">NodeHeight</span> <span class="p">+</span> <span class="n">childrenHeight</span><span class="p">);</span>
	<span class="k">return</span> <span class="n">NodeHeight</span> <span class="p">+</span> <span class="n">childrenHeight</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The sum of children’s width is the root’s width and the height is simply the depth of
the tree.</p>

<p>Along the way, we can store the whole size of the subtree, and we can calculate the positions
of each node later on.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="n">CGPoint</span> <span class="nf">calcTreePos</span> <span class="p">(</span><span class="n">JObject</span> <span class="n">root</span><span class="p">,</span> <span class="n">CGPoint</span> <span class="n">origin</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">root</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">return</span> <span class="n">origin</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="kt">int</span> <span class="n">width</span> <span class="p">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">root</span> <span class="p">[</span><span class="n">NODE_WIDTH_TAG</span><span class="p">];</span>
	<span class="kt">int</span> <span class="n">height</span> <span class="p">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">root</span> <span class="p">[</span><span class="n">NODE_HEIGHT_TAG</span><span class="p">];</span>

	<span class="c1">// root</span>
	<span class="n">CGPoint</span> <span class="n">rootPos</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">CGPoint</span><span class="p">(</span><span class="n">origin</span><span class="p">.</span><span class="n">X</span> <span class="p">+</span> <span class="n">width</span> <span class="p">/</span> <span class="m">2</span><span class="p">,</span> <span class="n">origin</span><span class="p">.</span><span class="n">Y</span> <span class="p">+</span> <span class="n">NodeHeight</span> <span class="p">/</span> <span class="m">2</span><span class="p">);</span>
	<span class="n">_nodes</span><span class="p">.</span><span class="nf">Add</span> <span class="p">(</span><span class="n">rootPos</span><span class="p">);</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">root</span> <span class="p">[</span><span class="s">"children"</span><span class="p">]</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
		<span class="kt">var</span> <span class="n">curX</span> <span class="p">=</span> <span class="n">origin</span><span class="p">.</span><span class="n">X</span><span class="p">;</span>
		<span class="kt">var</span> <span class="n">curY</span> <span class="p">=</span> <span class="n">origin</span><span class="p">.</span><span class="n">Y</span> <span class="p">+</span> <span class="n">NodeHeight</span><span class="p">;</span>
		<span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">child</span> <span class="k">in</span> <span class="n">root</span><span class="p">[</span><span class="s">"children"</span><span class="p">])</span> <span class="p">{</span>
			<span class="n">JObject</span> <span class="n">childObj</span> <span class="p">=</span> <span class="n">child</span><span class="p">.</span><span class="n">Value</span><span class="p">&lt;</span><span class="n">JObject</span><span class="p">&gt;</span> <span class="p">();</span>
			<span class="n">CGPoint</span> <span class="n">childPos</span> <span class="p">=</span> <span class="nf">calcTreePos</span><span class="p">(</span><span class="n">childObj</span><span class="p">,</span> <span class="k">new</span> <span class="nf">CGPoint</span><span class="p">(</span><span class="n">curX</span><span class="p">,</span> <span class="n">curY</span><span class="p">));</span>
			<span class="c1">// add path</span>
			<span class="n">_paths</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">Tuple</span><span class="p">&lt;</span><span class="n">CGPoint</span><span class="p">,</span> <span class="n">CGPoint</span><span class="p">&gt;(</span><span class="n">rootPos</span><span class="p">,</span> <span class="n">childPos</span><span class="p">));</span>
			<span class="kt">int</span> <span class="n">childWidth</span> <span class="p">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">childObj</span> <span class="p">[</span><span class="n">NODE_WIDTH_TAG</span><span class="p">];</span>
			<span class="n">curX</span> <span class="p">+=</span> <span class="n">childWidth</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span>

	<span class="k">return</span> <span class="n">rootPos</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="at-last">At last</h2>

<p>OK, the tree is very ugly now, more should be done the make it better.</p>]]></content><author><name>Guangming Mao</name></author><category term="blog" /><category term="C#" /><category term="Mac" /><summary type="html"><![CDATA[Lately I’m on a project with tree relation between some models, and those relations are store in the database, each row represents a parent-child link.]]></summary></entry></feed>