URI:
       kokada.dev.atom.xml - sfeed_tests - sfeed tests and RSS and Atom files
  HTML git clone git://git.codemadness.org/sfeed_tests
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       kokada.dev.atom.xml (494691B)
       ---
            1 <?xml version="1.0" encoding="utf-8"?>
            2 <feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>kokada</title><link href="https://kokada.dev" rel="alternate"/><link href="https://kokada.dev/atom/" rel="self"/><id>https://kokada.dev</id><updated>2024-12-04T00:00:00+00:00</updated><subtitle/><entry><title>Praise to scdoc to generate man pages</title><link href="https://kokada.dev/blog/praise-to-scdoc-to-generate-man-pages/" rel="alternate"/><published>2024-12-04T00:00:00+00:00</published><id>https://kokada.dev/blog/praise-to-scdoc-to-generate-man-pages/</id><summary type="html">&lt;p&gt;Hey, its been a long time since my &lt;a href="posts/2024-10-07/01-enabling-le-audio-lc3-in-wf-1000xm5.md"&gt;last blog
            3 post&lt;/a&gt;. It is mostly
            4 because I ran out of things to write, but I expected this. This is probably
            5 more likely how I am actually going to post from now. At least, it shows that
            6 my plan to have a &lt;a href="https://kokada.dev/blog/making-a-blog-for-the-next-10-years"&gt;blog for a long
            7 time&lt;/a&gt;, that is
            8 easy to go back when I wanted is working fine, but I digress.&lt;/p&gt;
            9 &lt;p&gt;Going back to the theme of the today blog post, I needed to write a &lt;a href="https://en.wikipedia.org/wiki/Man_page"&gt;man
           10 page&lt;/a&gt; for the first time in years. I
           11 hate &lt;a href="https://en.wikipedia.org/wiki/Troff"&gt;troff&lt;/a&gt;, the typesetting system used
           12 for man pages (similar to &lt;a href="https://en.wikipedia.org/wiki/LaTeX"&gt;LaTeX&lt;/a&gt; for
           13 documents). It is one of the weirdest languages that I ever saw, and even the
           14 example in Wikipedia shows that:&lt;/p&gt;
           15 &lt;pre&gt;&lt;code class="language-troff"&gt;.ND &amp;quot;January 10, 1993&amp;quot;
           16 .AU &amp;quot;Ms. Jane Smith&amp;quot;
           17 .AT &amp;quot;Upcoming appointment&amp;quot;
           18 .MT 5
           19 .DS
           20 Reference #A12345
           21 .sp 4
           22 Mr. Samuel Jones
           23 Field director, Bureau of Inspections
           24 1010 Government Plaza
           25 Capitoltown, ST
           26 .sp 3
           27 Dear Mr. Jones,
           28 .sp 2
           29 .P
           30 Making reference to the noted obligation to submit for state inspection our newly created production process, we request that you consider the possible inappropriateness of subjecting the  innovative technologies of tomorrow to the largely antiquated requirements of yesterday.  If our great state is to prosper in the twenty-first century, we must take steps
           31 .B now ,
           32 in
           33 .I this
           34 year of
           35 .I this
           36 decade, to prepare our industrial base for the interstate and international competition that is sure to appear.  Our new process does precisely that.  Please do not let it be undone by a regulatory environment that is no longer apt.
           37 .P
           38 Thank you for your consideration of our position.
           39 .FC Sincerely
           40 .SG
           41 &lt;/code&gt;&lt;/pre&gt;
           42 &lt;p&gt;Keep in mind that the break lines are necessary every time you introduce a
           43 macro, like &lt;code&gt;.I this&lt;/code&gt; (that I &lt;em&gt;think&lt;/em&gt; it is for italics). Yes, this format is
           44 as illegible as hell, and it is worse that the format lacks good tooling (or at
           45 least I didn't find any good ones).&lt;/p&gt;
           46 &lt;p&gt;Most people when they need to write a man page nowadays ends up using some
           47 other format that generates a man page. For example, in the past I used
           48 &lt;a href="https://pandoc.org/"&gt;Pandoc&lt;/a&gt; to convert Markdown to a man page, but even if
           49 Pandoc is a great project the result is sub-optimal at best: Markdowns are, at
           50 the end, designed for generating HTML (and a subset of it), and not man pages,
           51 so you basically ends up fighting the format for it to do what you want.
           52 Also, Pandoc is a big project, with a ~200MB binary (at least it is the default
           53 Pandoc binary in Nix).&lt;/p&gt;
           54 &lt;p&gt;For this specific project I needed something small. I am trying to replace one
           55 of the most essential pieces inside NixOS, &lt;code&gt;nixos-rebuild&lt;/code&gt;, written in Bash,
           56 with a &lt;a href="https://discourse.nixos.org/t/nixos-rebuild-ng-a-nixos-rebuild-rewrite/55606/"&gt;full rewritten in
           57 Python&lt;/a&gt;
           58 (sorry Rust zealots!), called &lt;code&gt;nixos-rebuild-ng&lt;/code&gt;.&lt;/p&gt;
           59 &lt;p&gt;Since this project will eventually (if successful) be in the critical path for
           60 NixOS, I want to reduce the number of dependencies as much as possible, so
           61 something as big as Pandoc is out. I could use
           62 &lt;a href="https://asciidoc.org/"&gt;AsciiDoc&lt;/a&gt;, but it is a big complicated Python project
           63 (this may seem ironic, but &lt;code&gt;nixos-rebuild-ng&lt;/code&gt; has only one runtime dependency,
           64 that is optional). And I also hated the last time I tried to use it to generate
           65 man pages: it more flexible than Markdown, but still far from optimal.&lt;/p&gt;
           66 &lt;p&gt;Thanks to Drew DeVault (creator of &lt;a href="https://swaywm.org/"&gt;SwayWM&lt;/a&gt;) that seems it
           67 had the same issues in the past and created
           68 &lt;a href="https://drewdevault.com/2018/05/13/scdoc.html"&gt;&lt;code&gt;scdoc&lt;/code&gt;&lt;/a&gt;, a very simple man
           69 page generator using a DSL inspired in Markdown, but specific to generate man
           70 pages. The binary is written in C (and advantage in this case since it means it
           71 is easier to bootstrap), is small (~1 Kloc) and has no dependencies, so it
           72 fits the requirement.&lt;/p&gt;
           73 &lt;p&gt;While the language suffers from being a niche project for a niche segment, the
           74 &lt;a href="https://man.archlinux.org/man/scdoc.5.en"&gt;man page&lt;/a&gt; for it is actually really
           75 nice. It is terse though and lacks examples, and this is what this blog post
           76 will try to accomplish.&lt;/p&gt;
           77 &lt;p&gt;To start, let's have a quick summary of the syntax, written in &lt;code&gt;scdoc&lt;/code&gt; as
           78 comments:&lt;/p&gt;
           79 &lt;pre&gt;&lt;code class="language-scdoc"&gt;; quick summary:
           80 ; # new section
           81 ; comments starts with ;
           82 ; - this is a list
           83 ;         - sub-list
           84 ; - *bold*: _underline_, force a line break++
           85 ; - [tables], \[ can be used to force an actual [
           86 ; . numbered list
           87 ; please configure your editor to use hard tabs
           88 ; see `man 5 scdoc` for more information about syntax
           89 ; or https://man.archlinux.org/man/scdoc.5.en
           90 &lt;/code&gt;&lt;/pre&gt;
           91 &lt;p&gt;I actually added this summary in the &lt;code&gt;.scd&lt;/code&gt; (the &lt;code&gt;scdoc&lt;/code&gt; extension) files that
           92 I wrote, so it is easy for someone that never saw the format to start
           93 collaborating.&lt;/p&gt;
           94 &lt;p&gt;And here an example of a (summarised) man page in &lt;code&gt;.scd&lt;/code&gt; format:&lt;/p&gt;
           95 &lt;pre class="chroma"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nixos-rebuild-ng(8)
           96 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
           97 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;# NAME
           98 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;&lt;/span&gt;
           99 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nixos-rebuild - reconfigure a NixOS machine
          100 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          101 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;# SYNOPSIS
          102 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;&lt;/span&gt;
          103 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="ge"&gt;_nixos-rebuild_&lt;/span&gt; \[--upgrade] [--upgrade-all]++
          104 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;                \[{switch,boot}]
          105 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          106 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;# DESCRIPTION
          107 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;&lt;/span&gt;
          108 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;This command has one required argument, which specifies the desired operation.
          109 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;It must be one of the following:
          110 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          111 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="ge"&gt;*switch*&lt;/span&gt;
          112 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        Build and activate the new configuration, and make it the boot default.
          113 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        That is, the configuration is added to the GRUB boot menu as the
          114 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        default menu entry, so that subsequent reboots will boot the system
          115 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        into the new configuration. Previous configurations activated with
          116 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        nixos-rebuild switch or nixos-rebuild boot remain available in the GRUB
          117 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        menu.
          118 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          119 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="ge"&gt;*boot*&lt;/span&gt;
          120 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        Build the new configuration and make it the boot default (as with
          121 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="ge"&gt;*nixos-rebuild switch*&lt;/span&gt;), but do not activate it. That is, the system
          122 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        continues to run the previous configuration until the next reboot.
          123 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          124 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;# OPTIONS
          125 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;&lt;/span&gt;
          126 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="ge"&gt;*--upgrade, --upgrade-all*&lt;/span&gt;
          127 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        Update the root user&amp;#39;s channel named &amp;#39;nixos&amp;#39; before rebuilding the
          128 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        system.
          129 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          130 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        In addition to the &amp;#39;nixos&amp;#39; channel, the root user&amp;#39;s channels which have
          131 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        a file named &amp;#39;.update-on-nixos-rebuild&amp;#39; in their base directory will
          132 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        also be updated.
          133 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          134 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        Passing &lt;span class="ge"&gt;*--upgrade-all*&lt;/span&gt; updates all of the root user&amp;#39;s channels.
          135 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          136 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;See the Nix manual, &lt;span class="ge"&gt;*nix flake lock --help*&lt;/span&gt; or &lt;span class="ge"&gt;*nix-build --help*&lt;/span&gt; for details.
          137 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          138 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;# ENVIRONMENT
          139 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;&lt;/span&gt;
          140 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NIXOS_CONFIG
          141 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        Path to the main NixOS configuration module. Defaults to
          142 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="ge"&gt;_/etc/nixos/configuration.nix_&lt;/span&gt;.
          143 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          144 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;# FILES
          145 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;&lt;/span&gt;
          146 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/etc/nixos/flake.nix
          147 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        If this file exists, then &lt;span class="ge"&gt;*nixos-rebuild*&lt;/span&gt; will use it as if the
          148 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="ge"&gt;*--flake*&lt;/span&gt; option was given. This file may be a symlink to a
          149 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        flake.nix in an actual flake; thus &lt;span class="ge"&gt;_/etc/nixos_&lt;/span&gt; need not be a
          150 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        flake.
          151 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          152 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;# AUTHORS
          153 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;&lt;/span&gt;
          154 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Nixpkgs/NixOS contributors
          155 &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And here is a screenshot of the result:&lt;/p&gt;
          156 &lt;p&gt;&lt;a href="https://github.com/thiagokokada/blog/raw/main/posts/2024-12-04/2024-12-04-230955_hyprshot.png"&gt;&lt;img src="https://github.com/thiagokokada/blog/raw/main/posts/2024-12-04/2024-12-04-230955_hyprshot.png" alt="Man page rendered from scd
          157 file"&gt;&lt;/a&gt;&lt;/p&gt;
          158 &lt;p&gt;One of nice things that I found is how looking at the plain text looks kind
          159 like the man page result already. And if you know Markdown, you can basically
          160 understand most things that is happening. There are a few differences, like
          161 &lt;code&gt;*bold*&lt;/code&gt; instead of &lt;code&gt;**bold**&lt;/code&gt;, and while they're unfortunate they're not the
          162 end of the world.&lt;/p&gt;
          163 &lt;p&gt;Now, the format has its quirks. The first line being the name of the program
          164 and section in parenthesis is required, but this makes sense, since you need
          165 this information for the corners. But for one, it requires the usage of hard
          166 tabs to create indentation, and the error messages are awful, in a situation
          167 that kind remembers me of &lt;code&gt;Makefile&lt;/code&gt;. Also the choice of &lt;code&gt;[&lt;/code&gt; to start a table
          168 means that the traditional &lt;code&gt;app [command]&lt;/code&gt; needs in many cases to be escaped as
          169 &lt;code&gt;app \[command]&lt;/code&gt;. I found this a strange choice since this is supposed to be a
          170 format that is only used for man pages, and using &lt;code&gt;[command]&lt;/code&gt; to indicate an
          171 optional is common, but at least it is easy to escape.&lt;/p&gt;
          172 &lt;p&gt;In the end, I think all that matters is the result. And for the first time for
          173 all those years trying to write a man page, I am satisfied with the result. The
          174 man page looks exactly as I wanted once rendered, and the &lt;code&gt;.scd&lt;/code&gt; file looks
          175 reasonable good that it can work as a documentation for someone that for one
          176 reason or another can't use the man page (can't say the same for the troff
          177 version). Also, it is really easy for someone to just go there and update the
          178 man page, even without experience in the format (except for maybe the
          179 requirement of tabs). So all in all, I really liked the format, and will use it
          180 again if I need to write another man page in the future.&lt;/p&gt;</summary></entry><entry><title>Enabling LE Audio/LC3 in WF-1000XM5</title><link href="https://kokada.dev/blog/enabling-le-audio-lc3-in-wf-1000xm5/" rel="alternate"/><published>2024-10-07T00:00:00+00:00</published><id>https://kokada.dev/blog/enabling-le-audio-lc3-in-wf-1000xm5/</id><summary type="html">&lt;p&gt;One of things that I hate the most about the fact that we are all using
          181 wireless earbuds instead of wired earphones is the latency: it is bad, getting
          182 up to seconds(!) depending on your particular combination of OS/earbuds/device.&lt;/p&gt;
          183 &lt;p&gt;There is a solution though: Bluetooth LE Audio, that is supposed to fix
          184 multiple issues with the original design for Bluetooth Classic Audio, including
          185 a much lower latency, improved efficiency (e.g.: less battery power) and even
          186 multiple streams of audio. LE Audio also includes a new default codec for
          187 improved audio quality, &lt;a href="https://en.wikipedia.org/wiki/LC3_(codec)"&gt;LC3&lt;/a&gt;, that
          188 replaces the venerable &lt;a href="https://en.wikipedia.org/wiki/SBC_(codec)"&gt;SBC&lt;/a&gt; codec
          189 for audio.&lt;/p&gt;
          190 &lt;p&gt;However, the standard is a mess right now: a few wireless headphones already
          191 support it, but they're generally disabled by default and it is pretty messy to
          192 enable. And even after enabling it, getting it to work can be a pain.&lt;/p&gt;
          193 &lt;p&gt;I have pretty much the best setup to use LE Audio right now: a recently
          194 released Pixel 9 Pro with Sony's
          195 &lt;a href="https://www.sony.ie/headphones/products/wf-1000xm5"&gt;WF-1000XM5&lt;/a&gt; earbuds, and
          196 after lots of tries I got it to work. You can see below the versions of
          197 everything I am using:&lt;/p&gt;
          198 &lt;ul&gt;
          199 &lt;li&gt;Android: 14&lt;/li&gt;
          200 &lt;li&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.sony.songpal.mdr"&gt;Sound
          201 Connect&lt;/a&gt;:
          202 11.0.1&lt;/li&gt;
          203 &lt;li&gt;WM-1000XM5: 4.0.2&lt;/li&gt;
          204 &lt;/ul&gt;
          205 &lt;p&gt;The first thing you need to do is enable in &amp;quot;Sound Connect&amp;quot; app &amp;quot;LE Audio
          206 Priority&amp;quot; in &amp;quot;Device Settings -&amp;gt; System&amp;quot;:&lt;/p&gt;
          207 &lt;p&gt;&lt;a href="https://github.com/thiagokokada/blog/raw/main/posts/2024-10-07/photo_4909454744305642922_y.jpg"&gt;&lt;img src="https://github.com/thiagokokada/blog/raw/main/posts/2024-10-07/photo_4909454744305642922_y.jpg" alt="LE Audio option inside Sound
          208 Connect"&gt;&lt;/a&gt;&lt;/p&gt;
          209 &lt;p&gt;After this, you will need to pair your headset with the device again. You can
          210 do this as same as always: press and hold the button in case for a few seconds
          211 until a blue light starts to blink. However, this is where things starts to get
          212 janky: I couldn't get the headset to pair with Android again.&lt;/p&gt;
          213 &lt;p&gt;A few of the things that I needed to do (in no specific order):&lt;/p&gt;
          214 &lt;ul&gt;
          215 &lt;li&gt;Remove the previous paired headset&lt;/li&gt;
          216 &lt;li&gt;Restart the Android&lt;/li&gt;
          217 &lt;li&gt;Clean-up &amp;quot;Sound Connect&amp;quot; storage (Long press the app icon -&amp;gt; &amp;quot;App info&amp;quot; -&amp;gt;
          218 &amp;quot;Storage and Cache&amp;quot; -&amp;gt; &amp;quot;Clear storage&amp;quot;)&lt;/li&gt;
          219 &lt;/ul&gt;
          220 &lt;p&gt;If you can get the headset to connect, go to the &amp;quot;Bluetooth&amp;quot; settings in
          221 Android, click in the gear icon for the headset and enable &amp;quot;LE Audio&amp;quot; option:&lt;/p&gt;
          222 &lt;p&gt;&lt;a href="https://github.com/thiagokokada/blog/raw/main/posts/2024-10-07/photo_4909454744305642937_y.jpg"&gt;&lt;img src="https://github.com/thiagokokada/blog/raw/main/posts/2024-10-07/photo_4909454744305642937_y.jpg" alt="LE Audio option Bluetooth
          223 Settings"&gt;&lt;/a&gt;&lt;/p&gt;
          224 &lt;p&gt;If you can't, you may want to &lt;a href="https://helpguide.sony.net/mdr/2963/v1/en/contents/TP1000783925.html"&gt;restore the headset to factory
          225 settings&lt;/a&gt;
          226 and try again from the start (that means pairing your device with &amp;quot;Sound
          227 Connect&amp;quot; again, and you may want to try to clear the storage before doing so).&lt;/p&gt;
          228 &lt;p&gt;Yes, the process is extremely janky, but I think this is why both &amp;quot;Sound
          229 Connect&amp;quot; and Android marks this feature as beta/experimental. And I still need
          230 to test the latency, but from my initial testing there are some glitches when
          231 the audio is only used for a short period of time (e.g.: Duolingo only enables
          232 the audio when the character is speaking). So I only recommend this if you want
          233 to test how LE Audio will behave, since it is clear that this needs more
          234 polish.&lt;/p&gt;</summary></entry><entry><title>Quick bits: where does Nix store Flake's trusted settings?</title><link href="https://kokada.dev/blog/quick-bits-where-does-nix-store-flakes-trusted-settings/" rel="alternate"/><published>2024-09-30T00:00:00+00:00</published><id>https://kokada.dev/blog/quick-bits-where-does-nix-store-flakes-trusted-settings/</id><summary type="html">&lt;p&gt;Have you ever run a &lt;code&gt;nix build&lt;/code&gt; command and had this prompt?&lt;/p&gt;
          235 &lt;pre class="chroma"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; nix run .#darwinActivations/Sekai-MacBook-Pro
          236 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;do you want to allow configuration setting &amp;#39;extra-substituters&amp;#39; to be set to &amp;#39;https://nix-community.cachix.org https://thiagokokada-nix-configs.cachix.org&amp;#39; (y/N)? y
          237 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;do you want to permanently mark this value as trusted (y/N)? y
          238 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And realise that you did/didn't want to mark this value as trusted? But where
          239 is this stored? Well, I had to look at the Nix source code to find the answer,
          240 since I can't find this anywhere in the web or in ChatGPT (but I am sure that
          241 now that I posted this it will eventually be searchable), and the answer can be
          242 found
          243 &lt;a href="https://github.com/NixOS/nix/blob/c116030605bf7fecd232d0ff3b6fe066f23e4620/src/libflake/flake/config.cc#L13-L16"&gt;here&lt;/a&gt;:&lt;/p&gt;
          244 &lt;pre class="chroma"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Path&lt;/span&gt; &lt;span class="nf"&gt;trustedListPath&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          245 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          246 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;getDataDir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/trusted-settings.json&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          247 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          248 &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Where is &lt;code&gt;getDataDir()&lt;/code&gt; though? I found the answer
          249 &lt;a href="https://github.com/NixOS/nix/blob/c116030605bf7fecd232d0ff3b6fe066f23e4620/src/libutil/users.cc#L52-L65"&gt;here&lt;/a&gt;:&lt;/p&gt;
          250 &lt;pre class="chroma"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Path&lt;/span&gt; &lt;span class="nf"&gt;getDataDir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          251 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          252 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;NIX_DATA_HOME&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          253 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          254 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          255 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          256 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;xdgDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;XDG_DATA_HOME&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          257 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xdgDir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          258 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;xdgDir&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/nix&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          259 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          260 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;getHome&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/.local/share/nix&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          261 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="p"&gt;}&lt;/span&gt;
          262 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
          263 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          264 &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So we solved the mystery:&lt;/p&gt;
          265 &lt;ul&gt;
          266 &lt;li&gt;If &lt;code&gt;NIX_DATA_HOME&lt;/code&gt; is set, the file will be in
          267 &lt;code&gt;$NIX_DATA_HOME/trusted-settings.json&lt;/code&gt;&lt;/li&gt;
          268 &lt;li&gt;If &lt;code&gt;XDG_DATA_HOME&lt;/code&gt; is set, the file will be in
          269 &lt;code&gt;$XDG_DATA_HOME/nix/trusted-settings.json&lt;/code&gt;&lt;/li&gt;
          270 &lt;li&gt;Otherwise Nix will fallback to &lt;code&gt;$HOME/.local/share/nix/trusted-settings.json&lt;/code&gt;&lt;/li&gt;
          271 &lt;/ul&gt;
          272 &lt;p&gt;By the way, if you don't know why you got this prompt, if &lt;code&gt;flake.nix&lt;/code&gt; has a
          273 &lt;code&gt;nixConfig&lt;/code&gt; attribute inside &lt;code&gt;outputs&lt;/code&gt; &lt;strong&gt;and&lt;/strong&gt; this &lt;code&gt;nixConfig&lt;/code&gt; is an unsafe
          274 attribute (like &lt;code&gt;extra-substituters&lt;/code&gt;) you will get this prompt, unless you set
          275 &lt;a href="https://nix.dev/manual/nix/2.23/command-ref/conf-file#conf-accept-flake-config"&gt;&lt;code&gt;accept-flake-config = true&lt;/code&gt;&lt;/a&gt;
          276 in your Nix configuration (please &lt;strong&gt;do not do this&lt;/strong&gt;, it is dangerous because
          277 it may allow running possible unsafe options without asking you first).&lt;/p&gt;
          278 &lt;p&gt;You can inspect the JSON file or delete it and Nix will prompt you again the
          279 next time you run a &lt;code&gt;nix&lt;/code&gt; command. And yes, saving this preference is
          280 controversial considering this is Nix, but having the power to set &lt;code&gt;nixConfig&lt;/code&gt;
          281 attributes is really powerful, and with great powers comes great
          282 responsibilities.&lt;/p&gt;</summary></entry><entry><title>My coding experience with LLM</title><link href="https://kokada.dev/blog/my-coding-experience-with-llm/" rel="alternate"/><published>2024-09-02T00:00:00+00:00</published><id>https://kokada.dev/blog/my-coding-experience-with-llm/</id><summary type="html">&lt;p&gt;I don't want to talk about AI. AI fatigue is real, with (garbage) AI content
          283 everywhere you go. However, I like to talk about tools, and AIs, or more
          284 specifically, &lt;a href="https://en.wikipedia.org/wiki/Large_language_model"&gt;Large Language
          285 Models&lt;/a&gt; (LLM), can be
          286 really useful tools. Maybe someday we will have our AI overlords dominating
          287 everything (including my job), but right now I don't feel particularly
          288 threatened given my experience with AIs.&lt;/p&gt;
          289 &lt;p&gt;Given that &lt;a href="https://mashable.com/article/google-search-low-quality-research"&gt;Google Search is getting
          290 worse&lt;/a&gt;, I am
          291 slowly relying in LLMs more and more. This is the focus of this post, basically
          292 &lt;a href="https://www.rottentomatoes.com/m/the_good_the_bad_and_the_ugly"&gt;the good, the bad and the
          293 ugly&lt;/a&gt; (by the
          294 way, never saw this film) of LLMs, especially focused for solving coding
          295 problems. I will take a look at the my chat history in
          296 &lt;a href="https://chatgpt.com/"&gt;ChatGPT&lt;/a&gt; and see where it was a good tool, where it
          297 shined, and where it just completely wasted my time.&lt;/p&gt;
          298 &lt;p&gt;A disclaimer, I never bothered to buy a ChatGPT subscription because I don't
          299 feel that the tool is useful enough (at least yet), so most of the examples
          300 here are either with &lt;a href="https://openai.com/index/chatgpt/"&gt;GPT-3.5&lt;/a&gt; or &lt;a href="https://openai.com/index/gpt-4o-mini-advancing-cost-efficient-intelligence/"&gt;GPT-4o
          301 mini&lt;/a&gt;,
          302 since those where the models offered by free in ChatGPT.&lt;/p&gt;
          303 &lt;p&gt;An addendum, I have some experience with &lt;a href="https://github.com/features/copilot"&gt;GitHub
          304 Copilot&lt;/a&gt; (another LLM, but this one runs
          305 inside your IDE or favorite text editor) in &lt;code&gt;$PREVIOUS_JOB&lt;/code&gt;. But my experience
          306 with it is even more underwhelming than what I am going to describe in this
          307 post: it was basically really good for generating fixture for tests and between
          308 ok-ish to plain bad for everything else. Yes, that's would fit in a tweet (or
          309 xeet?).&lt;/p&gt;
          310 &lt;h2&gt;The good&lt;/h2&gt;
          311 &lt;p&gt;&lt;a href="https://chatgpt.com/share/c58a1432-2761-4954-acac-cade29942c34"&gt;Chat history&lt;/a&gt;.&lt;/p&gt;
          312 &lt;blockquote&gt;
          313 &lt;p&gt;Is there any way that I can pass a context for a net.conn to cancel its
          314 Read()?&lt;/p&gt;
          315 &lt;/blockquote&gt;
          316 &lt;p&gt;This is one of the good cases where ChatGPT felt to me that I was talking with
          317 an expert in Go. To start, I didn't even mention in my initial prompt that I
          318 was talking about Go, but it correctly inferred so. Also, the solution overview
          319 was great:&lt;/p&gt;
          320 &lt;blockquote&gt;
          321 &lt;ol&gt;
          322 &lt;li&gt;&lt;strong&gt;Use a separate goroutine&lt;/strong&gt; to handle the context cancellation.&lt;/li&gt;
          323 &lt;li&gt;&lt;strong&gt;Set a deadline&lt;/strong&gt; on the connection using &lt;code&gt;SetReadDeadline()&lt;/code&gt; to unblock
          324 the &lt;code&gt;Read()&lt;/code&gt; when the context is canceled.&lt;/li&gt;
          325 &lt;/ol&gt;
          326 &lt;/blockquote&gt;
          327 &lt;p&gt;Before asking ChatGPT about this issue I had already coded my own naive
          328 solution that was something similar to this:&lt;/p&gt;
          329 &lt;pre class="chroma"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          330 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="c1"&gt;// ...
          331 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;    &lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
          332 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
          333 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
          334 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          335 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          336 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          337 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}{}&lt;/span&gt;
          338 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="p"&gt;}()&lt;/span&gt;
          339 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          340 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          341 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
          342 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          343 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          344 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          345 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
          346 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
          347 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="c1"&gt;// ...
          348 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          349 &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Compared this solution with the one from ChatGPT:&lt;/p&gt;
          350 &lt;pre class="chroma"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;readWithContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;conn&lt;/span&gt; &lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buf&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          351 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
          352 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
          353 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
          354 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          355 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="c1"&gt;// Start a goroutine to perform the read
          356 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          357 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          358 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          359 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="p"&gt;}()&lt;/span&gt;
          360 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          361 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          362 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="c1"&gt;// Context was canceled or timed out
          363 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;        &lt;span class="c1"&gt;// Set a short deadline to unblock the Read()
          364 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;        &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetReadDeadline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
          365 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="c1"&gt;// Wait for the read to finish
          366 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          367 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;// Read finished successfully
          368 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
          369 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
          370 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          371 &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The ChatGPT version is much better because:&lt;/p&gt;
          372 &lt;ol&gt;
          373 &lt;li&gt;It doesn't close the connection socket allowing it to be reused&lt;/li&gt;
          374 &lt;li&gt;It waits until the goroutine is finished to avoid leaks&lt;/li&gt;
          375 &lt;li&gt;It closes the channel after it finishes&lt;/li&gt;
          376 &lt;/ol&gt;
          377 &lt;p&gt;Now, I don't claim to be a Go expert (far from it), but this was one of those
          378 cases where I felt that I was being guided by someone that is coding in Go for
          379 years.&lt;/p&gt;
          380 &lt;p&gt;But it didn't end there, I asked:&lt;/p&gt;
          381 &lt;blockquote&gt;
          382 &lt;p&gt;If I want to reuse the connection, do I need to reset the SetReadDeadline?&lt;/p&gt;
          383 &lt;/blockquote&gt;
          384 &lt;p&gt;Since in this particular case I wanted the underlying connection to be reused,
          385 and ChatGPT correctly amended the code:&lt;/p&gt;
          386 &lt;pre class="chroma"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;readWithContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;conn&lt;/span&gt; &lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buf&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          387 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="c1"&gt;// Same as before
          388 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          389 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="c1"&gt;// Context was canceled or timed out
          390 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;        &lt;span class="c1"&gt;// Set a short deadline to unblock the Read()
          391 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;        &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetReadDeadline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
          392 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="c1"&gt;// Wait for the read to finish
          393 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;
          394 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="c1"&gt;// Reset the deadline so the connection can be reused
          395 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;        &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetReadDeadline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
          396 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          397 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;// Read finished successfully
          398 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
          399 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
          400 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          401 &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Also the explanation on why this happens was good:&lt;/p&gt;
          402 &lt;blockquote&gt;
          403 &lt;p&gt;When you set a read deadline with &lt;code&gt;conn.SetReadDeadline(time.Now())&lt;/code&gt; to unblock
          404 the &lt;code&gt;Read()&lt;/code&gt; call, this deadline remains in effect for future &lt;code&gt;Read()&lt;/code&gt;
          405 operations. This means that if you attempt another read operation after this,
          406 it could immediately fail with a timeout error if the deadline is still in
          407 effect.
          408 To reuse the connection, you'll need to remove the deadline or set a new one.
          409 You can do this by calling &lt;code&gt;conn.SetReadDeadline(time.Time{})&lt;/code&gt;, which clears
          410 any existing deadline.&lt;/p&gt;
          411 &lt;/blockquote&gt;
          412 &lt;p&gt;I had one last question that I kind already knew the answer:&lt;/p&gt;
          413 &lt;blockquote&gt;
          414 &lt;p&gt;Do we need to defer the clearing?&lt;/p&gt;
          415 &lt;/blockquote&gt;
          416 &lt;p&gt;And ChatGPT correctly returned:&lt;/p&gt;
          417 &lt;blockquote&gt;
          418 &lt;p&gt;Whether you need to defer the clearing of the read deadline depends on how
          419 your code is structured. If you want to ensure that the read deadline is
          420 always cleared after a context cancellation, even if the code panics or exits
          421 early, then deferring the &lt;code&gt;conn.SetReadDeadline(time.Time{})&lt;/code&gt; call is a good
          422 idea.&lt;/p&gt;
          423 &lt;/blockquote&gt;
          424 &lt;p&gt;This whole interaction was one of the best ones I ever had in ChatGPT, one that
          425 it gave me an actual functional code with good explanation on what was
          426 happening. But still, it didn't talked at all about the gotcha of setting the
          427 read deadline and reusing the underlying connection, needing another prompt to
          428 give me this information.&lt;/p&gt;
          429 &lt;p&gt;Also, there is one issue in ChatGPT solution though: we are discarding the
          430 values of the buffer once the context is done by returning &lt;code&gt;n = 0&lt;/code&gt; instead of
          431 the number of bytes read. In this particular case this is what I wanted, but
          432 this is a possible issue that ChatGPT didn't talk.&lt;/p&gt;
          433 &lt;p&gt;For comparison,
          434 &lt;a href="https://stackoverflow.com/questions/61637359/how-to-use-context-context-with-tcp-connection-read"&gt;this&lt;/a&gt;
          435 is the best that I got searching in both DuckDuckGo and Google, that is where I
          436 kind based my first solution on. I still not sure where ChatGPT could give me
          437 such a good answer for this question, because I just couldn't find anything
          438 remotely similar during my searches.&lt;/p&gt;
          439 &lt;h2&gt;The amazing&lt;/h2&gt;
          440 &lt;p&gt;&lt;a href="https://chatgpt.com/share/0c9a148f-4cfd-4b4e-aa84-7486e185125b"&gt;Chat history&lt;/a&gt;.&lt;/p&gt;
          441 &lt;blockquote&gt;
          442 &lt;p&gt;I want to write an extension to &lt;a href="https://github.com/yuin/goldmark/"&gt;https://github.com/yuin/goldmark/&lt;/a&gt; that allows
          443 me to rewrite &lt;code&gt;[link](../2024-07-30/post.md)&lt;/code&gt; to
          444 &lt;code&gt;[link](https://kokada.capivaras.dev/slug)&lt;/code&gt;&lt;/p&gt;
          445 &lt;/blockquote&gt;
          446 &lt;p&gt;Here is the generated code:&lt;/p&gt;
          447 &lt;pre class="chroma"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;linkrewriter&lt;/span&gt;
          448 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          449 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          450 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="s"&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
          451 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="s"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;
          452 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="s"&gt;&amp;#34;strings&amp;#34;&lt;/span&gt;
          453 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          454 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="s"&gt;&amp;#34;github.com/yuin/goldmark&amp;#34;&lt;/span&gt;
          455 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="s"&gt;&amp;#34;github.com/yuin/goldmark/ast&amp;#34;&lt;/span&gt;
          456 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="s"&gt;&amp;#34;github.com/yuin/goldmark/parser&amp;#34;&lt;/span&gt;
          457 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="s"&gt;&amp;#34;github.com/yuin/goldmark/renderer&amp;#34;&lt;/span&gt;
          458 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="s"&gt;&amp;#34;github.com/yuin/goldmark/util&amp;#34;&lt;/span&gt;
          459 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          460 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          461 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// LinkRewriter is the main struct for your extension
          462 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;LinkRewriter&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
          463 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          464 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// NewLinkRewriter returns a new instance of LinkRewriter
          465 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;NewLinkRewriter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;LinkRewriter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          466 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;LinkRewriter&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
          467 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          468 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          469 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Extend will be called by Goldmark to add your extension
          470 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;LinkRewriter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;Extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="nx"&gt;goldmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Markdown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          471 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;AddOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithASTTransformers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Prioritized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
          472 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          473 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
          474 &lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Transform is the method that modifies the AST
codemadness.org:70 /git/sfeed_tests/file/input/sfeed/realworld/kokada.dev.atom.xml.gph:485: line too long