aboutsummaryrefslogtreecommitdiff
path: root/docs/posts/rapid_refactoring_with_vim
diff options
context:
space:
mode:
Diffstat (limited to 'docs/posts/rapid_refactoring_with_vim')
-rw-r--r--docs/posts/rapid_refactoring_with_vim/index.html119
1 files changed, 91 insertions, 28 deletions
diff --git a/docs/posts/rapid_refactoring_with_vim/index.html b/docs/posts/rapid_refactoring_with_vim/index.html
index 3d867a3..e240da7 100644
--- a/docs/posts/rapid_refactoring_with_vim/index.html
+++ b/docs/posts/rapid_refactoring_with_vim/index.html
@@ -42,9 +42,14 @@
42 Rapid Refactoring With Vim 42 Rapid Refactoring With Vim
43 </h1> 43 </h1>
44 <div class="post-text"> 44 <div class="post-text">
45 <p>Last weekend, I was tasked with refactoring the 96 unit tests on <a href="https://github.com/ruma/ruma-events/pull/70">ruma-events</a> to use strictly typed json objects using <code>serde_json::json!</code> instead of raw strings. It was rather painless thanks to vim :)</p> 45 <p>Last weekend, I was tasked with refactoring the 96 unit tests on <a
46<p>Here’s a small sample of what had to be done (note the lines prefixed with the arrow):</p> 46href="https://github.com/ruma/ruma-events/pull/70">ruma-events</a> to
47<div class="sourceCode" id="cb1"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>→ <span class="kw">use</span> <span class="pp">serde_json::</span><span class="op">{</span>from_str<span class="op">};</span></span> 47use strictly typed json objects using <code>serde_json::json!</code>
48instead of raw strings. It was rather painless thanks to vim :)</p>
49<p>Here’s a small sample of what had to be done (note the lines prefixed
50with the arrow):</p>
51<div class="sourceCode" id="cb1"><pre
52class="sourceCode rust"><code class="sourceCode rust"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>→ <span class="kw">use</span> <span class="pp">serde_json::</span><span class="op">{</span>from_str<span class="op">};</span></span>
48<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> </span> 53<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> </span>
49<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="at">#[</span>test<span class="at">]</span></span> 54<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="at">#[</span>test<span class="at">]</span></span>
50<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> deserialize() <span class="op">{</span></span> 55<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> deserialize() <span class="op">{</span></span>
@@ -54,7 +59,8 @@
54<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> )<span class="op">;</span></span> 59<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> )<span class="op">;</span></span>
55<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span></code></pre></div> 60<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span></code></pre></div>
56<p>had to be converted to:</p> 61<p>had to be converted to:</p>
57<div class="sourceCode" id="cb2"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>→ <span class="kw">use</span> <span class="pp">serde_json::</span><span class="op">{</span>from_value<span class="op">};</span></span> 62<div class="sourceCode" id="cb2"><pre
63class="sourceCode rust"><code class="sourceCode rust"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>→ <span class="kw">use</span> <span class="pp">serde_json::</span><span class="op">{</span>from_value<span class="op">};</span></span>
58<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> </span> 64<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> </span>
59<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="at">#[</span>test<span class="at">]</span></span> 65<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="at">#[</span>test<span class="at">]</span></span>
60<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> deserialize() <span class="op">{</span></span> 66<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">fn</span> deserialize() <span class="op">{</span></span>
@@ -64,19 +70,38 @@
64<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> )<span class="op">;</span></span> 70<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> )<span class="op">;</span></span>
65<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span></code></pre></div> 71<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span></code></pre></div>
66<h3 id="the-arglist">The arglist</h3> 72<h3 id="the-arglist">The arglist</h3>
67<p>For the initial pass, I decided to handle imports, this was a simple find and replace operation, done to all the files containing tests. Luckily, modules (and therefore files) containing tests in Rust are annotated with the <code>#[cfg(test)]</code> attribute. I opened all such files:</p> 73<p>For the initial pass, I decided to handle imports, this was a simple
68<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co"># `grep -l pattern files` lists all the files</span></span> 74find and replace operation, done to all the files containing tests.
75Luckily, modules (and therefore files) containing tests in Rust are
76annotated with the <code>#[cfg(test)]</code> attribute. I opened all
77such files:</p>
78<div class="sourceCode" id="cb3"><pre
79class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co"># `grep -l pattern files` lists all the files</span></span>
69<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="co"># matching the pattern</span></span> 80<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="co"># matching the pattern</span></span>
70<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a></span> 81<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a></span>
71<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="ex">vim</span> <span class="va">$(</span><span class="fu">grep</span> <span class="at">-l</span> <span class="st">&#39;cfg\(test\)&#39;</span> ./<span class="pp">**</span>/<span class="pp">*</span>.rs<span class="va">)</span></span> 82<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="ex">vim</span> <span class="va">$(</span><span class="fu">grep</span> <span class="at">-l</span> <span class="st">&#39;cfg\(test\)&#39;</span> ./<span class="pp">**</span>/<span class="pp">*</span>.rs<span class="va">)</span></span>
72<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span> 83<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
73<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="co"># expands to something like:</span></span> 84<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="co"># expands to something like:</span></span>
74<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="ex">vim</span> push_rules.rs room/member.rs key/verification/lib.rs</span></code></pre></div> 85<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="ex">vim</span> push_rules.rs room/member.rs key/verification/lib.rs</span></code></pre></div>
75<p>Starting vim with more than one file at the shell prompt populates the arglist. Hit <code>:args</code> to see the list of files currently ready to edit. The square [brackets] indicate the current file. Navigate through the arglist with <code>:next</code> and <code>:prev</code>. I use tpope’s vim-unimpaired <a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>, which adds <code>]a</code> and <code>[a</code>, mapped to <code>:next</code> and <code>:prev</code>.</p> 86<p>Starting vim with more than one file at the shell prompt populates
76<p>All that’s left to do is the find and replace, for which we will be using vim’s <code>argdo</code>, applying a substitution to every file in the arglist:</p> 87the arglist. Hit <code>:args</code> to see the list of files currently
88ready to edit. The square [brackets] indicate the current file. Navigate
89through the arglist with <code>:next</code> and <code>:prev</code>. I
90use tpope’s vim-unimpaired <a href="#fn1" class="footnote-ref"
91id="fnref1" role="doc-noteref"><sup>1</sup></a>, which adds
92<code>]a</code> and <code>[a</code>, mapped to <code>:next</code> and
93<code>:prev</code>.</p>
94<p>All that’s left to do is the find and replace, for which we will be
95using vim’s <code>argdo</code>, applying a substitution to every file in
96the arglist:</p>
77<pre><code>:argdo s/from_str/from_value/g</code></pre> 97<pre><code>:argdo s/from_str/from_value/g</code></pre>
78<h3 id="the-quickfix-list">The quickfix list</h3> 98<h3 id="the-quickfix-list">The quickfix list</h3>
79<p>Next up, replacing <code>r#" ... "#</code> with <code>json!( ... )</code>. I couldn’t search and replace that trivially, so I went with a macro call <a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> instead, starting with the cursor on ‘r’, represented by the caret, in my attempt to breakdown the process:</p> 99<p>Next up, replacing <code>r#" ... "#</code> with
100<code>json!( ... )</code>. I couldn’t search and replace that trivially,
101so I went with a macro call <a href="#fn2" class="footnote-ref"
102id="fnref2" role="doc-noteref"><sup>2</sup></a> instead, starting with
103the cursor on ‘r’, represented by the caret, in my attempt to breakdown
104the process:</p>
80<pre><code>BUFFER: r#&quot; ... &quot;#; 105<pre><code>BUFFER: r#&quot; ... &quot;#;
81 ^ 106 ^
82 107
@@ -93,42 +118,80 @@ BUFFER: json!( ... &quot;#;
93ACTION: vhs)&lt;esc&gt; 118ACTION: vhs)&lt;esc&gt;
94 119
95BUFFER: json!( ... );</code></pre> 120BUFFER: json!( ... );</code></pre>
96<p>Here’s the recorded <a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> macro in all its glory: <code>vllsjson!(&lt;esc&gt;$F#vhs)&lt;esc&gt;</code>.</p> 121<p>Here’s the recorded <a href="#fn3" class="footnote-ref" id="fnref3"
97<p>Great! So now we just go ahead, find every occurrence of <code>r#</code> and apply the macro right? Unfortunately, there were more than a few occurrences of raw strings that had to stay raw strings. Enter, the quickfix list.</p> 122role="doc-noteref"><sup>3</sup></a> macro in all its glory:
98<p>The idea behind the quickfix list is to jump from one position in a file to another (maybe in a different file), much like how the arglist lets you jump from one file to another.</p> 123<code>vllsjson!(&lt;esc&gt;$F#vhs)&lt;esc&gt;</code>.</p>
99<p>One of the easiest ways to populate this list with a bunch of positions is to use <code>vimgrep</code>:</p> 124<p>Great! So now we just go ahead, find every occurrence of
125<code>r#</code> and apply the macro right? Unfortunately, there were
126more than a few occurrences of raw strings that had to stay raw strings.
127Enter, the quickfix list.</p>
128<p>The idea behind the quickfix list is to jump from one position in a
129file to another (maybe in a different file), much like how the arglist
130lets you jump from one file to another.</p>
131<p>One of the easiest ways to populate this list with a bunch of
132positions is to use <code>vimgrep</code>:</p>
100<pre><code># basic usage 133<pre><code># basic usage
101:vimgrep pattern files 134:vimgrep pattern files
102 135
103# search for raw strings 136# search for raw strings
104:vimgrep &#39;r#&#39; ./**/*.rs</code></pre> 137:vimgrep &#39;r#&#39; ./**/*.rs</code></pre>
105<p>Like <code>:next</code> and <code>:prev</code>, you can navigate the quickfix list with <code>:cnext</code> and <code>:cprev</code>. Every time you move up or down the list, vim indicates your index:</p> 138<p>Like <code>:next</code> and <code>:prev</code>, you can navigate the
139quickfix list with <code>:cnext</code> and <code>:cprev</code>. Every
140time you move up or down the list, vim indicates your index:</p>
106<pre><code>(1 of 131): r#&quot;{&quot;set_tweak&quot;: &quot;highlight&quot;}&quot;#;</code></pre> 141<pre><code>(1 of 131): r#&quot;{&quot;set_tweak&quot;: &quot;highlight&quot;}&quot;#;</code></pre>
107<p>And just like <code>argdo</code>, you can <code>cdo</code> to apply commands to <em>every</em> match in the quickfix list:</p> 142<p>And just like <code>argdo</code>, you can <code>cdo</code> to apply
143commands to <em>every</em> match in the quickfix list:</p>
108<pre><code>:cdo norm! @q</code></pre> 144<pre><code>:cdo norm! @q</code></pre>
109<p>But, I had to manually pick out matches, and it involved some button mashing.</p> 145<p>But, I had to manually pick out matches, and it involved some button
146mashing.</p>
110<h3 id="external-filtering">External Filtering</h3> 147<h3 id="external-filtering">External Filtering</h3>
111<p>Some code reviews later, I was asked to format all the json inside the <code>json!</code> macro. All you have to do is pass a visual selection through a pretty json printer. Select the range to be formatted in visual mode, and hit <code>:</code>, you will notice the command line displaying what seems to be gibberish:</p> 148<p>Some code reviews later, I was asked to format all the json inside
149the <code>json!</code> macro. All you have to do is pass a visual
150selection through a pretty json printer. Select the range to be
151formatted in visual mode, and hit <code>:</code>, you will notice the
152command line displaying what seems to be gibberish:</p>
112<pre><code>:&#39;&lt;,&#39;&gt;</code></pre> 153<pre><code>:&#39;&lt;,&#39;&gt;</code></pre>
113<p><code>'&lt;</code> and <code>'&gt;</code> are <em>marks</em> <a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a>. More specifically, they are marks that vim sets automatically every time you make a visual selection, denoting the start and end of the selection.</p> 154<p><code>'&lt;</code> and <code>'&gt;</code> are <em>marks</em> <a
114<p>A range is one or more line specifiers separated by a <code>,</code>:</p> 155href="#fn4" class="footnote-ref" id="fnref4"
156role="doc-noteref"><sup>4</sup></a>. More specifically, they are marks
157that vim sets automatically every time you make a visual selection,
158denoting the start and end of the selection.</p>
159<p>A range is one or more line specifiers separated by a
160<code>,</code>:</p>
115<pre><code>:1,7 lines 1 through 7 161<pre><code>:1,7 lines 1 through 7
116:32 just line 32 162:32 just line 32
117:. the current line 163:. the current line
118:.,$ the current line to the last line 164:.,$ the current line to the last line
119:&#39;a,&#39;b mark &#39;a&#39; to mark &#39;b&#39;</code></pre> 165:&#39;a,&#39;b mark &#39;a&#39; to mark &#39;b&#39;</code></pre>
120<p>Most <code>:</code> commands can be prefixed by ranges. <code>:help usr_10.txt</code> for more on that.</p> 166<p>Most <code>:</code> commands can be prefixed by ranges.
121<p>Alright, lets pass json through <code>python -m json.tool</code>, a json formatter that accepts <code>stdin</code> (note the use of <code>!</code> to make use of an external program):</p> 167<code>:help usr_10.txt</code> for more on that.</p>
168<p>Alright, lets pass json through <code>python -m json.tool</code>, a
169json formatter that accepts <code>stdin</code> (note the use of
170<code>!</code> to make use of an external program):</p>
122<pre><code>:&#39;&lt;,&#39;&gt;!python -m json.tool</code></pre> 171<pre><code>:&#39;&lt;,&#39;&gt;!python -m json.tool</code></pre>
123<p>Unfortunately that didn’t quite work for me because the range included some non-json text as well, a mix of regex and macros helped fix that. I think you get the drift.</p> 172<p>Unfortunately that didn’t quite work for me because the range
124<p>Another fun filter I use from time to time is <code>:!sort</code>, to sort css attributes, or <code>:!uniq</code> to remove repeated imports.</p> 173included some non-json text as well, a mix of regex and macros helped
125<section class="footnotes" role="doc-endnotes"> 174fix that. I think you get the drift.</p>
175<p>Another fun filter I use from time to time is <code>:!sort</code>, to
176sort css attributes, or <code>:!uniq</code> to remove repeated
177imports.</p>
178<section id="footnotes" class="footnotes footnotes-end-of-document"
179role="doc-endnotes">
126<hr /> 180<hr />
127<ol> 181<ol>
128<li id="fn1" role="doc-endnote"><p>https://github.com/tpope/vim-unimpaired It also handles various other mappings, <code>]q</code> and <code>[q</code> to navigate the quickfix list for example<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li> 182<li id="fn1"><p>https://github.com/tpope/vim-unimpaired It also handles
129<li id="fn2" role="doc-endnote"><p><code>:help recording</code><a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li> 183various other mappings, <code>]q</code> and <code>[q</code> to navigate
130<li id="fn3" role="doc-endnote"><p>When I’m recording a macro, I prefer starting out by storing it in register <code>q</code>, and then copying it over to another register if it works as intended. I think of <code>qq</code> as ‘quick record’.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li> 184the quickfix list for example<a href="#fnref1" class="footnote-back"
131<li id="fn4" role="doc-endnote"><p><code>:help mark-motions</code><a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li> 185role="doc-backlink">↩︎</a></p></li>
186<li id="fn2"><p><code>:help recording</code><a href="#fnref2"
187class="footnote-back" role="doc-backlink">↩︎</a></p></li>
188<li id="fn3"><p>When I’m recording a macro, I prefer starting out by
189storing it in register <code>q</code>, and then copying it over to
190another register if it works as intended. I think of <code>qq</code> as
191‘quick record’.<a href="#fnref3" class="footnote-back"
192role="doc-backlink">↩︎</a></p></li>
193<li id="fn4"><p><code>:help mark-motions</code><a href="#fnref4"
194class="footnote-back" role="doc-backlink">↩︎</a></p></li>
132</ol> 195</ol>
133</section> 196</section>
134 197