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.html261
1 files changed, 261 insertions, 0 deletions
diff --git a/docs/posts/rapid_refactoring_with_vim/index.html b/docs/posts/rapid_refactoring_with_vim/index.html
new file mode 100644
index 0000000..4e33a31
--- /dev/null
+++ b/docs/posts/rapid_refactoring_with_vim/index.html
@@ -0,0 +1,261 @@
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <link rel="stylesheet" href="/style.css">
5 <meta charset="UTF-8">
6 <meta name="viewport" content="initial-scale=1">
7 <meta content="#ffffff" name="theme-color">
8 <meta name="HandheldFriendly" content="true">
9 <meta property="og:title" content="Rapid Refactoring With Vim">
10 <meta property="og:type" content="website">
11 <meta property="og:description" content="a static site {for, by, about} me ">
12 <meta property="og:url" content="https://peppe.rs">
13 <link rel="icon" type="image/x-icon" href="/favicon.png">
14 <title>Rapid Refactoring With Vim · peppe.rs</title>
15 <body>
16 <div class="posts">
17 <div class="post">
18 <a href="/" class="post-end-link">⟵ Back</a>
19 <a class="stats post-end-link" href="https://raw.githubusercontent.com/nerdypepper/site/master/posts/rapid_refactoring_with_vim.md
20">View Raw</a>
21 <div class="separator"></div>
22 <div class="date">
23 01/04 — 2020
24 <div class="stats">
25 <span class="stats-number">
26 79.12
27 </span>
28 <span class="stats-unit">cm</span>
29 &nbsp
30 <span class="stats-number">
31 5.4
32 </span>
33 <span class="stats-unit">min</span>
34 </div>
35 </div>
36 <span class="post-title">
37 Rapid Refactoring With Vim
38 </span>
39 <div class="post-text">
40 <p>Last weekend, I was tasked with refactoring the 96 unit
41tests on
42<a href="https://github.com/ruma/ruma-events/pull/70">ruma-events</a>
43to use strictly typed json objects using <code>serde_json::json!</code>
44instead of raw strings. It was rather painless thanks to
45vim :)</p>
46
47<p>Here&#39;s a small sample of what had to be done (note the lines
48prefixed with the arrow):</p>
49
50<pre><code>→ use serde_json::{from_str};
51
52 #[test]
53 fn deserialize() {
54 assert_eq!(
55→ from_str::&#60;Action&#62;(r#&#34;{&#34;set_tweak&#34;: &#34;highlight&#34;}&#34;#),
56 Action::SetTweak(Tweak::Highlight { value: true })
57 );
58 }
59</code></pre>
60
61<p>had to be converted to:</p>
62
63<pre><code>→ use serde_json::{from_value};
64
65 #[test]
66 fn deserialize() {
67 assert_eq!(
68→ from_value::&#60;Action&#62;(json!({&#34;set_tweak&#34;: &#34;highlight&#34;})),
69 Action::SetTweak(Tweak::Highlight { value: true })
70 );
71 }
72</code></pre>
73
74<h2 id="The%20arglist">The arglist</h2>
75
76<p>For the initial pass, I decided to handle imports, this was
77a simple find and replace operation, done to all the files
78containing tests. Luckily, modules (and therefore files)
79containing tests in Rust are annotated with the
80<code>#[cfg(test)]</code> attribute. I opened all such files:</p>
81
82<pre><code># `grep -l pattern files` lists all the files
83# matching the pattern
84
85vim $(grep -l &#39;cfg\(test\)&#39; .&#47;**&#47;*.rs)
86
87# expands to something like:
88vim push_rules.rs room&#47;member.rs key&#47;verification&#47;lib.rs
89</code></pre>
90
91<p>Starting vim with more than one file at the shell prompt
92populates the arglist. Hit <code>:args</code> to see the list of
93files currently ready to edit. The square [brackets]
94indicate the current file. Navigate through the arglist
95with <code>:next</code> and <code>:prev</code>. I use tpope&#39;s vim-unimpaired
96<sup id="fnref1"><a href="#fn1" rel="footnote">1</a></sup>, which adds <code>]a</code> and <code>[a</code>, mapped to <code>:next</code> and
97<code>:prev</code>.</p>
98
99<p>All that&#39;s left to do is the find and replace, for which we
100will be using vim&#39;s <code>argdo</code>, applying a substitution to
101every file in the arglist:</p>
102
103<pre><code>:argdo s&#47;from_str&#47;from_value&#47;g
104</code></pre>
105
106<h2 id="The%20quickfix%20list">The quickfix list</h2>
107
108<p>Next up, replacing <code>r#&#34; ... &#34;#</code> with <code>json!( ... )</code>. I
109couldn&#39;t search and replace that trivially, so I went with a
110macro call <sup id="fnref2"><a href="#fn2" rel="footnote">2</a></sup> instead, starting with the cursor on
111&#8216;r&#8217;, represented by the caret, in my attempt to breakdown
112the process:</p>
113
114<pre><code>BUFFER: r#&#34; ... &#34;#;
115 ^
116
117ACTION: vllsjson!(
118
119BUFFER json!( ... &#34;#;
120 ^
121
122ACTION: &#60;esc&#62;$F#
123
124BUFFER: json!( ... &#34;#;
125 ^
126
127ACTION: vhs)&#60;esc&#62;
128
129BUFFER: json!( ... );
130</code></pre>
131
132<p>Here&#39;s the recorded <sup id="fnref3"><a href="#fn3" rel="footnote">3</a></sup> macro in all its glory:
133<code>vllsjson!(&#60;esc&#62;$F#vhs)&#60;esc&#62;</code>. </p>
134
135<p>Great! So now we just go ahead, find every occurrence of
136<code>r#</code> and apply the macro right? Unfortunately, there were
137more than a few occurrences of raw strings that had to stay
138raw strings. Enter, the quickfix list.</p>
139
140<p>The idea behind the quickfix list is to jump from one
141position in a file to another (maybe in a different file),
142much like how the arglist lets you jump from one file to
143another.</p>
144
145<p>One of the easiest ways to populate this list with a bunch
146of positions is to use <code>vimgrep</code>:</p>
147
148<pre><code># basic usage
149:vimgrep pattern files
150
151# search for raw strings
152:vimgrep &#39;r#&#39; .&#47;**&#47;*.rs
153</code></pre>
154
155<p>Like <code>:next</code> and <code>:prev</code>, you can navigate the quickfix list
156with <code>:cnext</code> and <code>:cprev</code>. Every time you move up or down
157the list, vim indicates your index:</p>
158
159<pre><code>(1 of 131): r#&#34;{&#34;set_tweak&#34;: &#34;highlight&#34;}&#34;#;
160</code></pre>
161
162<p>And just like <code>argdo</code>, you can <code>cdo</code> to apply commands to
163<em>every</em> match in the quickfix list:</p>
164
165<pre><code>:cdo norm! @q
166</code></pre>
167
168<p>But, I had to manually pick out matches, and it involved
169some button mashing.</p>
170
171<h2 id="External%20Filtering">External Filtering</h2>
172
173<p>Some code reviews later, I was asked to format all the json
174inside the <code>json!</code> macro. All you have to do is pass a
175visual selection through a pretty json printer. Select the
176range to be formatted in visual mode, and hit <code>:</code>, you will
177notice the command line displaying what seems to be
178gibberish:</p>
179
180<pre><code>:&#39;&#60;,&#39;&#62;
181</code></pre>
182
183<p><code>&#39;&#60;</code> and <code>&#39;&#62;</code> are <em>marks</em> <sup id="fnref4"><a href="#fn4" rel="footnote">4</a></sup>. More
184specifically, they are marks that vim sets automatically
185every time you make a visual selection, denoting the start
186and end of the selection.</p>
187
188<p>A range is one or more line specifiers separated by a <code>,</code>:</p>
189
190<pre><code>:1,7 lines 1 through 7
191:32 just line 32
192:. the current line
193:.,$ the current line to the last line
194:&#39;a,&#39;b mark &#39;a&#39; to mark &#39;b&#39;
195</code></pre>
196
197<p>Most <code>:</code> commands can be prefixed by ranges. <code>:help
198usr_10.txt</code> for more on that.</p>
199
200<p>Alright, lets pass json through <code>python -m json.tool</code>, a
201json formatter that accepts <code>stdin</code> (note the use of <code>!</code> to
202make use of an external program):</p>
203
204<pre><code>:&#39;&#60;,&#39;&#62;!python -m json.tool
205</code></pre>
206
207<p>Unfortunately that didn&#39;t quite work for me because the
208range included some non-json text as well, a mix of regex
209and macros helped fix that. I think you get the drift.</p>
210
211<p>Another fun filter I use from time to time is <code>:!sort</code>, to
212sort css attributes, or <code>:!uniq</code> to remove repeated imports.</p>
213
214<div class="footnotes">
215<hr/>
216<ol>
217
218<li id="fn1">
219<p><a href="https://github.com/tpope/vim-unimpaired">https:&#47;&#47;github.com&#47;tpope&#47;vim-unimpaired</a>
220It also handles various other mappings, <code>]q</code> and <code>[q</code> to
221navigate the quickfix list for example&#160;<a href="#fnref1" rev="footnote">&#8617;</a></p>
222</li>
223
224<li id="fn2">
225<p><code>:help recording</code>&#160;<a href="#fnref2" rev="footnote">&#8617;</a></p>
226</li>
227
228<li id="fn3">
229<p>When I&#39;m recording a macro, I prefer starting out by
230storing it in register <code>q</code>, and then copying it over to
231another register if it works as intended. I think of <code>qq</code> as
232&#8216;quick record&#8217;.&#160;<a href="#fnref3" rev="footnote">&#8617;</a></p>
233</li>
234
235<li id="fn4">
236<p><code>:help mark-motions</code>&#160;<a href="#fnref4" rev="footnote">&#8617;</a></p>
237</li>
238
239</ol>
240</div>
241
242 </div>
243
244 <div class=intro>
245 Hi. <a href=https://peppe.rs/index.xml class=feed-button>Subscribe</a>
246 <p>I'm Akshay, I go by nerd or nerdypepper on the internet.</p>
247 <p>
248 I am a compsci undergrad, Rust programmer and an enthusiastic Vimmer.
249 I write open-source stuff to pass time. I also design fonts: scientifica, curie.
250 Things I find cool usually end up here.
251 </p>
252 <p>Get in touch at [email protected] or [email protected].</p>
253 </div>
254
255 <a href="/" class="post-end-link">⟵ Back</a>
256 <a class="stats post-end-link" href="https://raw.githubusercontent.com/nerdypepper/site/master/posts/rapid_refactoring_with_vim.md
257">View Raw</a>
258 </div>
259 </div>
260 </body>
261</html>