summaryrefslogtreecommitdiffstats
path: root/2023/info/parallel-after.md
blob: 16e689b69063f6cdec8605edc8ca49a013c284ad (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
<!-- Automatically generated by emacsconf-publish-after-page -->


<a name="parallel-mainVideo-transcript"></a>
# Transcript


[[!template new="1" text="""Introduction""" start="00:00:00.000" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""Hi everyone!""" start="00:00:00.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Welcome to our talk on Parallel Text Replacement.""" start="00:00:01.640" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""My name is Lovro, and I'll be telling you about an""" start="00:00:04.640" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""interesting problem that my friend Valentino and I""" start="00:00:07.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""set out to solve one afternoon.""" start="00:00:09.360" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We will describe the problem, take a look at some""" start="00:00:11.760" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""of the existing work and then present our solution.""" start="00:00:13.680" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Afterwards, we will show some demos and conclude""" start="00:00:16.880" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""with a quick overview of the implementation.""" start="00:00:19.080" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Let's get straight into it!""" start="00:00:21.520" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Problem: Goal""" start="00:00:23.440" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""Here is a problem that most of us have dealt with""" start="00:00:23.440" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""at some point.""" start="00:00:25.800" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Assume we have a piece of code such as the following.""" start="00:00:27.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We use a code example here, but in general what we're""" start="00:00:29.880" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""about to discuss can be applied to any piece of text.""" start="00:00:32.520" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""After a bit of thinking, we decide that the names of""" start="00:00:35.600" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""the two variables, &quot;foo&quot; and &quot;bar&quot;, should actually be""" start="00:00:37.640" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""swapped.""" start="00:00:39.960" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""That is, &quot;foo&quot; should be replaced with &quot;bar&quot;, and &quot;bar&quot;""" start="00:00:40.880" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""should be replaced with &quot;foo&quot;.""" start="00:00:43.560" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""The question is: what is a good way to achieve this?""" start="00:00:45.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We could perform the edits manually if the code is""" start="00:00:49.080" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""small enough, and we might even be done reasonably""" start="00:00:51.760" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""quickly.""" start="00:00:53.880" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""However, consider two things.""" start="00:00:54.720" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Imagine the usual case where there's just too much""" start="00:00:56.720" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""code to edit by hand.""" start="00:00:58.960" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We have no other option than to automate the task.""" start="00:01:00.760" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""More importantly though, we have a whole programmable""" start="00:01:03.680" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""text editor right at our fingertips.""" start="00:01:06.120" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We should object to doing things that the computer""" start="00:01:08.280" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""can do for us.""" start="00:01:10.280" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Problem: Naive Multi-pass""" start="00:01:12.360" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""So, one way to automate it is by using our old friend""" start="00:01:12.360" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""query-replace (M-%) multiple times in a sequence.""" start="00:01:15.560" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We first do a pass where we replace &quot;foo&quot; with &quot;bar&quot;,""" start="00:01:19.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""then we do another pass where we replace &quot;bar&quot; with &quot;foo&quot;.""" start="00:01:22.240" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""But that's clearly not right.""" start="00:01:25.640" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We all know that this naive multi-pass approach""" start="00:01:26.960" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""doesn't work because it results in interference""" start="00:01:29.160" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""between the two replacements.""" start="00:01:31.560" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Problem: Clever Multi-pass""" start="00:01:34.200" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""Instead, we have to be a bit more clever.""" start="00:01:34.200" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We should first replace &quot;foo&quot; with a temporary string,""" start="00:01:36.800" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""in this case &quot;oof&quot;, that we will call a &quot;token&quot;.""" start="00:01:39.840" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""To avoid interference, we must be careful to ensure""" start="00:01:42.120" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""that the token does not contain whatever we're about""" start="00:01:45.480" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""to replace next.""" start="00:01:48.120" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Then we do a second pass to replace &quot;bar&quot; with &quot;foo&quot;,""" start="00:01:49.600" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""and finally a third pass to replace the token with &quot;bar&quot;.""" start="00:01:52.720" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""This gives us the result we want.""" start="00:01:56.080" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Problem: Terminology""" start="00:01:57.720" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""Putting the implementation aside for a moment, this style""" start="00:01:57.720" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""of text replacement, where we replace multiple sources""" start="00:02:01.920" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""with their targets, without running into interference""" start="00:02:05.600" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""issues between replacement pairs, is what we call""" start="00:02:09.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""a &quot;parallel replacement&quot;.""" start="00:02:11.760" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""This is the essence of the problem we're trying to solve.""" start="00:02:12.840" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""The examples with swapping that we've shown so far""" start="00:02:16.360" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""are really just one of the many use cases that are""" start="00:02:18.680" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""supported by a general parallel replacement utility.""" start="00:02:21.320" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""To avoid confusion, let us clarify that the word &quot;parallel&quot;""" start="00:02:25.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""is not in reference to hardware parallelization, but""" start="00:02:28.760" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""rather comes from analogy with the Lisp let operator,""" start="00:02:31.760" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""where the bindings of variables are performed in parallel,""" start="00:02:34.880" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""rather than sequentially as in let*.""" start="00:02:38.160" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Parallel in this context means that none of the bindings""" start="00:02:40.200" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""are in scope within any of the initial value forms.""" start="00:02:43.680" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""In other words, just like a let's initialization form""" start="00:02:46.880" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""cannot refer to any of the earlier bindings, a""" start="00:02:50.200" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""replacement pair's source should not be able to replace""" start="00:02:53.720" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""the previously substituted targets of any other pair.""" start="00:02:56.760" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""This is what we mean by &quot;no interference&quot;.""" start="00:03:00.200" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Problem: Scaling Multi-pass""" start="00:03:04.440" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""However, manually invoking multiple carefully chosen""" start="00:03:04.440" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""query-replace commands gets old very quickly.""" start="00:03:08.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Say we scaled up the problem and wanted to perform n""" start="00:03:11.520" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""swaps instead of just two, e.g. to swap, or rather,""" start="00:03:14.200" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""rotate, &quot;foo&quot; to &quot;bar&quot;, &quot;bar&quot; to &quot;baz&quot;, &quot;baz&quot; to &quot;quux&quot;""" start="00:03:18.320" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""and &quot;quux&quot; to &quot;foo&quot;.""" start="00:03:22.160" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We would first have to perform n - 1 additional""" start="00:03:23.800" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""replacements to introduce the necessary tokens,""" start="00:03:26.360" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""effectively doubling the number of steps.""" start="00:03:29.240" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Even if we tried to automate this, think about what""" start="00:03:32.240" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""tokens the code would have to generate if we had no""" start="00:03:34.800" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""prior knowledge of the replacement pairs given by the""" start="00:03:37.680" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""user.""" start="00:03:40.520" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We would have to program defensively and use long""" start="00:03:41.560" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""randomly-generated strings that, one, hopefully do""" start="00:03:44.160" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""not interfere with any of the replacement pairs,""" start="00:03:47.560" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""and two, might slow down the search if they're overly long.""" start="00:03:50.280" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Can we do better?""" start="00:03:53.480" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Solution: Single-pass""" start="00:03:55.920" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""Yes we can!""" start="00:03:55.920" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We can actually perform just a single pass.""" start="00:03:56.840" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""The trick is to alternate between the replacement""" start="00:03:59.680" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""pairs, replacing whichever source occurs the earliest,""" start="00:04:02.280" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""and making sure to continue scanning after the end""" start="00:04:06.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""of the substituted target in order to avoid interference.""" start="00:04:08.440" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""This interleaving of replacements is not something""" start="00:04:12.280" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""that's easy to do by hand with query-replace.""" start="00:04:14.520" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Solution: Existing""" start="00:04:18.240" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""Since this is Emacs we're talking about, of course""" start="00:04:18.240" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""there already exist solutions that implement this idea.""" start="00:04:20.960" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Here are few that we could find.""" start="00:04:23.560" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""The EmacsWiki has a page dedicated to this problem.""" start="00:04:25.960" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Stack Overflow has an old post where a couple of""" start="00:04:28.800" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""users provided their solutions.""" start="00:04:31.560" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Mastering Emacs also gives a method along with other""" start="00:04:33.960" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""interesting query-replace-regexp (C-M-%) patterns.""" start="00:04:36.920" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""More recently, Tony Zorman made a blogpost providing""" start="00:04:39.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""a solution with an interface based on query-replace.""" start="00:04:42.360" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""I encourage you to take a look at these solutions if""" start="00:04:45.080" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""you're interested in the details.""" start="00:04:47.640" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""But while a step in the right direction, these solutions""" start="00:04:50.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""are not satisfactory because they all lack one or""" start="00:04:53.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""more of the following.""" start="00:04:55.440" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""One, they are not completely automated and require""" start="00:04:56.920" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""the user to come up with a relatively complicated""" start="00:05:00.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""and verbose query-replace-regexp invocation.""" start="00:05:02.600" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Two, they are restricted to performing only 2-element""" start="00:05:06.080" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""swaps rather than general parallel replacements.""" start="00:05:09.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Three, they don't provide any sort of interactivity""" start="00:05:12.680" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""during replacement and instead perform it in one shot.""" start="00:05:15.160" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Four, they don't attempt to integrate with the familiar""" start="00:05:18.620" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""query-replace interface, which supports skipping, undo,""" start="00:05:21.400" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""history and more advanced features like Lisp expressions""" start="00:05:25.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""and recursive query edits.""" start="00:05:28.440" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Most importantly however, five, none of them were""" start="00:05:30.700" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""designed with regular expressions in mind and instead""" start="00:05:33.800" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""only ever consider literal strings.""" start="00:05:36.480" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""In fact, the only one that comes close is the""" start="00:05:39.560" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""half-automated solution that invokes query-replace-regexp""" start="00:05:43.160" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""with a specially crafted replacement.""" start="00:05:46.520" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""As an example, here's how you would use this technique""" start="00:05:48.800" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""to perform a 3-element parallel regex replacement.""" start="00:05:51.760" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""It uses the backslash-comma Lisp expression feature""" start="00:05:54.440" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""in order to choose the appropriate target to substitute.""" start="00:05:57.840" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Aside from being very clumsy and tedious to write out,""" start="00:06:01.280" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""this approach makes it really hard to use more complex""" start="00:06:03.800" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""regular expressions that make use of capture groups""" start="00:06:06.960" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""themselves.""" start="00:06:09.360" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""This was the biggest limitation that we wanted""" start="00:06:10.800" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""to get rid of and the main motivation for our work.""" start="00:06:12.200" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""So, as an alternative to the existing zoo of 80% solutions,""" start="00:06:15.720" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""we aim to provide a 100% solution, one that handles""" start="00:06:19.920" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""regexes and consolidates all of the existing ideas""" start="00:06:24.240" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""into a single package.""" start="00:06:27.120" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Solution: query-replace-parallel""" start="00:06:29.080" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""We call it query-replace-parallel.""" start="00:06:29.080" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""The package is free and open-source and can currently""" start="00:06:31.360" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""be found on GitHub under hokomo/query-replace-parallel.""" start="00:06:34.160" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""The name is not yet finalized and we're open to any""" start="00:06:37.400" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""suggestions.""" start="00:06:40.240" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We hope to get it published on an Elisp""" start="00:06:41.503" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""package archive in the near future, but for now you""" start="00:06:43.280" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""can just download and load the main Elisp file manually.""" start="00:06:45.880" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""With all of that said, let's go through a few demos""" start="00:06:48.900" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""to illustrate some use cases and see how to use the package.""" start="00:06:51.400" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Demonstration: Swap""" start="00:06:55.240" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""Our first demo is a simple swap, like the one we""" start="00:06:55.240" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""showed at the beginning of the presentation.""" start="00:06:57.560" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""This chunk of text is actually one of the tests""" start="00:06:59.240" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""from our package's code.""" start="00:07:02.160" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Assuming we have loaded  the package, we can execute""" start="00:07:03.840" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""the query-replace-parallel command, a parallel version""" start="00:07:06.520" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""of the standard query-replace.""" start="00:07:09.680" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""This command works with literal strings and will""" start="00:07:11.320" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""ask for each source and target in turn.""" start="00:07:14.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Our goal is to replace &quot;foo&quot; with &quot;bar&quot;""" start="00:07:16.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""and &quot;bar&quot; with &quot;foo&quot;.""" start="00:07:21.360" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""After inputting our replacements, we terminate the""" start="00:07:24.680" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""prompt by pressing enter with empty input.""" start="00:07:27.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""At this point, everything functions the same as in""" start="00:07:29.860" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""a standard query-replace invocation.""" start="00:07:32.600" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""The echo area shows the match and the replacement""" start="00:07:35.280" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""we're about to make.""" start="00:07:37.400" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We can perform replacements,""" start="00:07:38.703" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""undo them,""" start="00:07:43.920" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""skip them,""" start="00:07:46.503" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""execute them until the end,""" start="00:07:49.203" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""and so on.""" start="00:07:50.240" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Demonstration: LaTeX""" start="00:07:53.970" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""The second demo shows our first regex use case.""" start="00:07:53.970" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Imagine we have the following LaTeX code.""" start="00:07:56.280" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We realize that we haven't been completely consistent""" start="00:07:58.720" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""in our use and naming of macros, so we decide to""" start="00:08:01.480" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""fix the problem.""" start="00:08:04.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""This time we execute query-replace-parallel-regexp""" start="00:08:05.536" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""because we want to work with regex instead of literal""" start="00:08:08.400" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""strings.""" start="00:08:11.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We want to achieve two things.""" start="00:08:12.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""First, we want to wrap all usages of the variable n""" start="00:08:13.520" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""with the natvar macro.""" start="00:08:16.960" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Using the backslash-less-than and blackslash-greater-than""" start="00:08:18.080" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""constructs allows us to only match letters n not""" start="00:08:21.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""appearing as part of a larger word.""" start="00:08:23.840" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Second, we want to rename natvar to intvar because""" start="00:08:25.360" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""the variables a, b and c are integers and not natural""" start="00:08:29.560" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""numbers.""" start="00:08:32.280" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We enter empty input to terminate the prompt and can""" start="00:08:33.300" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""now perform the replacements.""" start="00:08:35.760" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""There we go, the fixes are done and we didn't have""" start="00:08:42.280" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""to think about in which order to apply them.""" start="00:08:44.480" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Demonstration: Regex""" start="00:08:48.700" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""We now take a look at a more complicated regex""" start="00:08:48.700" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""example to demonstrate that even advanced query-replace""" start="00:08:51.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""features are supported.""" start="00:08:53.680" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Each &quot;foo&quot; and &quot;bar&quot; in this example is followed by""" start="00:08:55.100" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""a number.""" start="00:08:57.440" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""The goal is to not only swap &quot;foo&quot; and &quot;bar&quot;, but""" start="00:08:58.440" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""also increase or decrease the corresponding number.""" start="00:09:01.380" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We first match &quot;foo&quot; and capture the number that""" start="00:09:03.720" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""follows it.""" start="00:09:06.600" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""For the target, we make use of the backslash-comma""" start="00:09:07.200" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Lisp expression feature in order to replace the""" start="00:09:10.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""match with &quot;bar&quot; followed by the number's successor.""" start="00:09:12.600" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We do the same thing for &quot;bar&quot;, except that we""" start="00:09:15.540" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""replace the number with its predecessor.""" start="00:09:17.640" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Performing the replacements, we can see how each""" start="00:09:27.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""number is incremented or decremented appropriately.""" start="00:09:29.120" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Demonstration: Order""" start="00:09:36.320" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""We haven't covered it explicitly so some of you may""" start="00:09:36.320" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""be wondering how parallel replacement deals with""" start="00:09:38.760" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""overlapping matches and whether the order of the""" start="00:09:41.360" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""replacement pairs is significant.""" start="00:09:43.840" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""This demo will clarify the exact behavior.""" start="00:09:45.480" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""The first example has the sources &quot;watch&quot; and &quot;stopwatch&quot;.""" start="00:09:48.960" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Conceptually, the matches overlap, but the rule is""" start="00:09:57.500" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""that matches are always processed earliest first,""" start="00:10:00.600" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""regardless of their length or the ordering of the pairs.""" start="00:10:03.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Therefore it is &quot;stopwatch&quot; that gets replaced,""" start="00:10:06.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""and not its substring &quot;watch&quot;.""" start="00:10:09.080" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""The second example uses the sources &quot;watch&quot; and &quot;watchword&quot;.""" start="00:10:16.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Both of the matches now conceptually start at the same""" start="00:10:19.640" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""position.""" start="00:10:22.640" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""In situations like these the order of the pairs does""" start="00:10:23.720" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""matter, and ties are broken by prefering the pair that""" start="00:10:26.400" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""was entered first, which is behavior that is inherited""" start="00:10:29.560" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""from the Elisp regex engine.""" start="00:10:32.280" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""So, the substring &quot;watch&quot; in &quot;watchword&quot; is what gets""" start="00:10:34.460" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""replaced in this case.""" start="00:10:37.480" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Situations where the order of the pairs is significant""" start="00:10:39.460" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""are not very common however, so the user generally""" start="00:10:41.840" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""doesn't have to worry about this edge case.""" start="00:10:44.840" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""The order only matters when two or more sources""" start="00:10:46.760" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""share the same prefix, as in this example.""" start="00:10:49.960" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Demonstration: Fun""" start="00:10:54.440" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""The final demo tests the limits of the package and""" start="00:10:54.440" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""shows that it fully integrates with query-replace.""" start="00:10:56.960" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""It is really just for fun and can even serve as a""" start="00:10:59.760" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""small Emacs brainteaser.""" start="00:11:03.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""See if you can keep up!""" start="00:11:04.240" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We open a directory and enter Writable Dired mode""" start="00:11:06.360" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""in order to rename the directories &quot;foo&quot; and &quot;bar&quot;.""" start="00:11:09.160" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Instead of doing it quickly by hand, we decide to""" start="00:11:11.880" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""show off and use query-replace-parallel-regexp.""" start="00:11:14.760" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We enter our pairs and make use of the""" start="00:11:17.360" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""backslash-question-mark query edit feature.""" start="00:11:20.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Now whenever we perform a replacement, the query""" start="00:11:25.080" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""edit makes Emacs stop and prompt us for additional""" start="00:11:27.920" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""input to use as the target.""" start="00:11:30.840" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We confirm the renames and now enter the &quot;bar-lib&quot;""" start="00:11:36.680" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""directory in order to perform the same kind of""" start="00:11:39.240" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""replacement on &quot;baz&quot; and &quot;quux&quot;.""" start="00:11:42.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Rather than save time, we decide to be extra lazy""" start="00:11:44.500" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""and take the long route.""" start="00:11:47.920" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We recall the first pair and initiate a recursive""" start="00:11:48.920" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""invocation of query-replace-parallel-regexp.""" start="00:11:52.320" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We are now replacing the replacement.""" start="00:11:54.560" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We apply our fixes and then do the same thing again""" start="00:12:01.020" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""with the second pair.""" start="00:12:04.640" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Recall and recurse.""" start="00:12:05.970" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We confirm the prompt and finally rename our directories.""" start="00:12:16.300" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Wow, that really paid off.""" start="00:12:25.360" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""Implementation""" start="00:12:29.120" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""Before we finish, a few quick words about the""" start="00:12:29.120" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""implementation for the curious.""" start="00:12:31.480" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Both query-replace-parallel and query-replace-parallel-regexp""" start="00:12:33.300" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""delegate to the complex perform-replace function""" start="00:12:36.480" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""which is the workhorse of query-replace's interactive""" start="00:12:39.240" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""mechanism.""" start="00:12:41.880" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""The way we achieve multiple interleaved replacements""" start="00:12:43.120" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""is by providing perform-replace with a big &quot;matcher regex&quot;""" start="00:12:45.520" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""and a special replacement function.""" start="00:12:49.120" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Essentially, a complex parallel replacement like this""" start="00:12:50.480" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""is transformed into a standard replacement like this.""" start="00:12:54.400" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""This is similar to the trick shown earlier in the""" start="00:12:57.520" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""presentation.""" start="00:13:00.200" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Each source is put in its own capture group to allow""" start="00:13:00.880" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""the replacement function to determine which one matched""" start="00:13:03.920" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""and return the appropriate target.""" start="00:13:06.440" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""However, we now take care to support arbitrary""" start="00:13:08.980" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""regular expressions as sources.""" start="00:13:11.680" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We achieve this by converting each source regex into""" start="00:13:13.480" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""an equivalent one for which we can guarantee that its""" start="00:13:17.080" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""capture groups will not clash with our matcher regex.""" start="00:13:19.920" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Information about this conversion is stored, and""" start="00:13:22.920" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""once the replacement function is called it has""" start="00:13:26.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""enough data to apply the replacement from the""" start="00:13:28.320" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""viewpoint of the original regex.""" start="00:13:30.360" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""The regex transformation is reliable because it""" start="00:13:32.720" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""uses the rx library, allowing us to treat regexes""" start="00:13:35.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""as s-expressions and avoid any nasty manual parsing.""" start="00:13:38.520" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""In fact, rx itself is based on one of Olin Shivers'""" start="00:13:42.640" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""100% solutions:""" start="00:13:46.640" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""SRE, or the S-expression regex notation.""" start="00:13:48.436" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""We all stand on the shoulders of many giants, so""" start="00:13:51.320" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""let's strive to design good solutions that we can""" start="00:13:54.440" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""all benefit from, many years into the future!""" start="00:13:56.600" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Finally, because query-replace's core is not completely""" start="00:13:59.240" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""customizable, we did have to sprinkle in some advice""" start="00:14:03.000" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""to get certain things working.""" start="00:14:06.160" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""This concerns only minor cosmetic fixes and not the""" start="00:14:07.600" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""core replacement functionality, but we have nontheless""" start="00:14:11.160" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""tried to do it in the simplest and least intrusive way""" start="00:14:14.040" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""possible.""" start="00:14:16.680" video="mainVideo-parallel" id="subtitle"]]

[[!template new="1" text="""End""" start="00:14:18.740" video="mainVideo-parallel" id="subtitle"]]

[[!template text="""In conclusion, go download and play with the package.""" start="00:14:18.740" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Even if you're not performing overlapping replacements,""" start="00:14:21.680" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""you can still use query-replace-parallel for the""" start="00:14:24.560" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""peace of mind knowing that things won't go wrong if""" start="00:14:26.880" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""you perform more than one replacement at a time.""" start="00:14:29.720" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Feel free to let us know about any interesting or""" start="00:14:32.460" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""crazy use cases you might come up with, as well as""" start="00:14:34.640" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""improvements or bugs that make it only a 99% solution.""" start="00:14:37.560" video="mainVideo-parallel" id="subtitle"]]
[[!template text="""Thanks for listening and have a great EmacsConf!""" start="00:14:40.640" video="mainVideo-parallel" id="subtitle"]]

Questions or comments? Please e-mail [hokomo@disroot.org](mailto:hokomo@disroot.org?subject=Comment%20for%20EmacsConf%202023%20parallel%3A%20Parallel%20text%20replacement)


<!-- End of emacsconf-publish-after-page -->