diff options
Diffstat (limited to 'docs/posts/rapid_refactoring_with_vim')
-rw-r--r-- | docs/posts/rapid_refactoring_with_vim/index.html | 261 |
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 |   | ||
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 | ||
41 | tests on | ||
42 | <a href="https://github.com/ruma/ruma-events/pull/70">ruma-events</a> | ||
43 | to use strictly typed json objects using <code>serde_json::json!</code> | ||
44 | instead of raw strings. It was rather painless thanks to | ||
45 | vim :)</p> | ||
46 | |||
47 | <p>Here's a small sample of what had to be done (note the lines | ||
48 | prefixed 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::<Action>(r#"{"set_tweak": "highlight"}"#), | ||
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::<Action>(json!({"set_tweak": "highlight"})), | ||
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 | ||
77 | a simple find and replace operation, done to all the files | ||
78 | containing tests. Luckily, modules (and therefore files) | ||
79 | containing 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 | |||
85 | vim $(grep -l 'cfg\(test\)' ./**/*.rs) | ||
86 | |||
87 | # expands to something like: | ||
88 | vim push_rules.rs room/member.rs key/verification/lib.rs | ||
89 | </code></pre> | ||
90 | |||
91 | <p>Starting vim with more than one file at the shell prompt | ||
92 | populates the arglist. Hit <code>:args</code> to see the list of | ||
93 | files currently ready to edit. The square [brackets] | ||
94 | indicate the current file. Navigate through the arglist | ||
95 | with <code>:next</code> and <code>:prev</code>. I use tpope'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's left to do is the find and replace, for which we | ||
100 | will be using vim's <code>argdo</code>, applying a substitution to | ||
101 | every file in the arglist:</p> | ||
102 | |||
103 | <pre><code>:argdo s/from_str/from_value/g | ||
104 | </code></pre> | ||
105 | |||
106 | <h2 id="The%20quickfix%20list">The quickfix list</h2> | ||
107 | |||
108 | <p>Next up, replacing <code>r#" ... "#</code> with <code>json!( ... )</code>. I | ||
109 | couldn't search and replace that trivially, so I went with a | ||
110 | macro call <sup id="fnref2"><a href="#fn2" rel="footnote">2</a></sup> instead, starting with the cursor on | ||
111 | ‘r’, represented by the caret, in my attempt to breakdown | ||
112 | the process:</p> | ||
113 | |||
114 | <pre><code>BUFFER: r#" ... "#; | ||
115 | ^ | ||
116 | |||
117 | ACTION: vllsjson!( | ||
118 | |||
119 | BUFFER json!( ... "#; | ||
120 | ^ | ||
121 | |||
122 | ACTION: <esc>$F# | ||
123 | |||
124 | BUFFER: json!( ... "#; | ||
125 | ^ | ||
126 | |||
127 | ACTION: vhs)<esc> | ||
128 | |||
129 | BUFFER: json!( ... ); | ||
130 | </code></pre> | ||
131 | |||
132 | <p>Here's the recorded <sup id="fnref3"><a href="#fn3" rel="footnote">3</a></sup> macro in all its glory: | ||
133 | <code>vllsjson!(<esc>$F#vhs)<esc></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 | ||
137 | more than a few occurrences of raw strings that had to stay | ||
138 | raw strings. Enter, the quickfix list.</p> | ||
139 | |||
140 | <p>The idea behind the quickfix list is to jump from one | ||
141 | position in a file to another (maybe in a different file), | ||
142 | much like how the arglist lets you jump from one file to | ||
143 | another.</p> | ||
144 | |||
145 | <p>One of the easiest ways to populate this list with a bunch | ||
146 | of 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 'r#' ./**/*.rs | ||
153 | </code></pre> | ||
154 | |||
155 | <p>Like <code>:next</code> and <code>:prev</code>, you can navigate the quickfix list | ||
156 | with <code>:cnext</code> and <code>:cprev</code>. Every time you move up or down | ||
157 | the list, vim indicates your index:</p> | ||
158 | |||
159 | <pre><code>(1 of 131): r#"{"set_tweak": "highlight"}"#; | ||
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 | ||
169 | some 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 | ||
174 | inside the <code>json!</code> macro. All you have to do is pass a | ||
175 | visual selection through a pretty json printer. Select the | ||
176 | range to be formatted in visual mode, and hit <code>:</code>, you will | ||
177 | notice the command line displaying what seems to be | ||
178 | gibberish:</p> | ||
179 | |||
180 | <pre><code>:'<,'> | ||
181 | </code></pre> | ||
182 | |||
183 | <p><code>'<</code> and <code>'></code> are <em>marks</em> <sup id="fnref4"><a href="#fn4" rel="footnote">4</a></sup>. More | ||
184 | specifically, they are marks that vim sets automatically | ||
185 | every time you make a visual selection, denoting the start | ||
186 | and 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 | :'a,'b mark 'a' to mark 'b' | ||
195 | </code></pre> | ||
196 | |||
197 | <p>Most <code>:</code> commands can be prefixed by ranges. <code>:help | ||
198 | usr_10.txt</code> for more on that.</p> | ||
199 | |||
200 | <p>Alright, lets pass json through <code>python -m json.tool</code>, a | ||
201 | json formatter that accepts <code>stdin</code> (note the use of <code>!</code> to | ||
202 | make use of an external program):</p> | ||
203 | |||
204 | <pre><code>:'<,'>!python -m json.tool | ||
205 | </code></pre> | ||
206 | |||
207 | <p>Unfortunately that didn't quite work for me because the | ||
208 | range included some non-json text as well, a mix of regex | ||
209 | and 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 | ||
212 | sort 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://github.com/tpope/vim-unimpaired</a> | ||
220 | It also handles various other mappings, <code>]q</code> and <code>[q</code> to | ||
221 | navigate the quickfix list for example <a href="#fnref1" rev="footnote">↩</a></p> | ||
222 | </li> | ||
223 | |||
224 | <li id="fn2"> | ||
225 | <p><code>:help recording</code> <a href="#fnref2" rev="footnote">↩</a></p> | ||
226 | </li> | ||
227 | |||
228 | <li id="fn3"> | ||
229 | <p>When I'm recording a macro, I prefer starting out by | ||
230 | storing it in register <code>q</code>, and then copying it over to | ||
231 | another register if it works as intended. I think of <code>qq</code> as | ||
232 | ‘quick record’. <a href="#fnref3" rev="footnote">↩</a></p> | ||
233 | </li> | ||
234 | |||
235 | <li id="fn4"> | ||
236 | <p><code>:help mark-motions</code> <a href="#fnref4" rev="footnote">↩</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> | ||