WEBVTT captioned by Lovro
NOTE Introduction
00:00:00.000 --> 00:00:01.540
Hi everyone!
00:00:01.640 --> 00:00:04.540
Welcome to our talk on Parallel Text Replacement.
00:00:04.640 --> 00:00:06.940
My name is Lovro, and I'll be telling you about an
00:00:07.040 --> 00:00:09.260
interesting problem that my friend Valentino and I
00:00:09.360 --> 00:00:11.660
set out to solve one afternoon.
00:00:11.760 --> 00:00:13.580
We will describe the problem, take a look at some
00:00:13.680 --> 00:00:16.780
of the existing work and then present our solution.
00:00:16.880 --> 00:00:18.980
Afterwards, we will show some demos and conclude
00:00:19.080 --> 00:00:21.420
with a quick overview of the implementation.
00:00:21.520 --> 00:00:23.340
Let's get straight into it!
NOTE Problem: Goal
00:00:23.440 --> 00:00:25.700
Here is a problem that most of us have dealt with
00:00:25.800 --> 00:00:26.940
at some point.
00:00:27.040 --> 00:00:29.780
Assume we have a piece of code such as the following.
00:00:29.880 --> 00:00:32.420
We use a code example here, but in general what we're
00:00:32.520 --> 00:00:35.500
about to discuss can be applied to any piece of text.
00:00:35.600 --> 00:00:37.540
After a bit of thinking, we decide that the names of
00:00:37.640 --> 00:00:39.860
the two variables, "foo" and "bar", should actually be
00:00:39.960 --> 00:00:40.780
swapped.
00:00:40.880 --> 00:00:43.460
That is, "foo" should be replaced with "bar", and "bar"
00:00:43.560 --> 00:00:44.940
should be replaced with "foo".
00:00:45.040 --> 00:00:48.980
The question is: what is a good way to achieve this?
00:00:49.080 --> 00:00:51.660
We could perform the edits manually if the code is
00:00:51.760 --> 00:00:53.780
small enough, and we might even be done reasonably
00:00:53.880 --> 00:00:54.620
quickly.
00:00:54.720 --> 00:00:56.620
However, consider two things.
00:00:56.720 --> 00:00:58.860
Imagine the usual case where there's just too much
00:00:58.960 --> 00:01:00.660
code to edit by hand.
00:01:00.760 --> 00:01:03.580
We have no other option than to automate the task.
00:01:03.680 --> 00:01:06.020
More importantly though, we have a whole programmable
00:01:06.120 --> 00:01:08.180
text editor right at our fingertips.
00:01:08.280 --> 00:01:10.180
We should object to doing things that the computer
00:01:10.280 --> 00:01:12.260
can do for us.
NOTE Problem: Naive Multi-pass
00:01:12.360 --> 00:01:15.460
So, one way to automate it is by using our old friend
00:01:15.560 --> 00:01:18.940
query-replace (M-%) multiple times in a sequence.
00:01:19.040 --> 00:01:22.140
We first do a pass where we replace "foo" with "bar",
00:01:22.240 --> 00:01:25.540
then we do another pass where we replace "bar" with "foo".
00:01:25.640 --> 00:01:26.860
But that's clearly not right.
00:01:26.960 --> 00:01:29.060
We all know that this naive multi-pass approach
00:01:29.160 --> 00:01:31.460
doesn't work because it results in interference
00:01:31.560 --> 00:01:34.100
between the two replacements.
NOTE Problem: Clever Multi-pass
00:01:34.200 --> 00:01:36.700
Instead, we have to be a bit more clever.
00:01:36.800 --> 00:01:39.740
We should first replace "foo" with a temporary string,
00:01:39.840 --> 00:01:42.020
in this case "oof", that we will call a "token".
00:01:42.120 --> 00:01:45.380
To avoid interference, we must be careful to ensure
00:01:45.480 --> 00:01:48.020
that the token does not contain whatever we're about
00:01:48.120 --> 00:01:49.500
to replace next.
00:01:49.600 --> 00:01:52.620
Then we do a second pass to replace "bar" with "foo",
00:01:52.720 --> 00:01:55.980
and finally a third pass to replace the token with "bar".
00:01:56.080 --> 00:01:57.620
This gives us the result we want.
NOTE Problem: Terminology
00:01:57.720 --> 00:02:01.820
Putting the implementation aside for a moment, this style
00:02:01.920 --> 00:02:05.500
of text replacement, where we replace multiple sources
00:02:05.600 --> 00:02:08.940
with their targets, without running into interference
00:02:09.040 --> 00:02:11.660
issues between replacement pairs, is what we call
00:02:11.760 --> 00:02:12.740
a "parallel replacement".
00:02:12.840 --> 00:02:16.260
This is the essence of the problem we're trying to solve.
00:02:16.360 --> 00:02:18.580
The examples with swapping that we've shown so far
00:02:18.680 --> 00:02:21.220
are really just one of the many use cases that are
00:02:21.320 --> 00:02:23.740
supported by a general parallel replacement utility.
00:02:25.040 --> 00:02:28.660
To avoid confusion, let us clarify that the word "parallel"
00:02:28.760 --> 00:02:31.660
is not in reference to hardware parallelization, but
00:02:31.760 --> 00:02:34.780
rather comes from analogy with the Lisp let operator,
00:02:34.880 --> 00:02:38.060
where the bindings of variables are performed in parallel,
00:02:38.160 --> 00:02:40.100
rather than sequentially as in let*.
00:02:40.200 --> 00:02:43.580
Parallel in this context means that none of the bindings
00:02:43.680 --> 00:02:46.780
are in scope within any of the initial value forms.
00:02:46.880 --> 00:02:50.100
In other words, just like a let's initialization form
00:02:50.200 --> 00:02:53.620
cannot refer to any of the earlier bindings, a
00:02:53.720 --> 00:02:56.660
replacement pair's source should not be able to replace
00:02:56.760 --> 00:03:00.100
the previously substituted targets of any other pair.
00:03:00.200 --> 00:03:03.340
This is what we mean by "no interference".
NOTE Problem: Scaling Multi-pass
00:03:04.440 --> 00:03:07.900
However, manually invoking multiple carefully chosen
00:03:08.000 --> 00:03:11.420
query-replace commands gets old very quickly.
00:03:11.520 --> 00:03:14.100
Say we scaled up the problem and wanted to perform n
00:03:14.200 --> 00:03:18.220
swaps instead of just two, e.g. to swap, or rather,
00:03:18.320 --> 00:03:22.060
rotate, "foo" to "bar", "bar" to "baz", "baz" to "quux"
00:03:22.160 --> 00:03:23.700
and "quux" to "foo".
00:03:23.800 --> 00:03:26.260
We would first have to perform n - 1 additional
00:03:26.360 --> 00:03:29.140
replacements to introduce the necessary tokens,
00:03:29.240 --> 00:03:32.140
effectively doubling the number of steps.
00:03:32.240 --> 00:03:34.700
Even if we tried to automate this, think about what
00:03:34.800 --> 00:03:37.580
tokens the code would have to generate if we had no
00:03:37.680 --> 00:03:40.420
prior knowledge of the replacement pairs given by the
00:03:40.520 --> 00:03:41.460
user.
00:03:41.560 --> 00:03:44.060
We would have to program defensively and use long
00:03:44.160 --> 00:03:47.460
randomly-generated strings that, one, hopefully do
00:03:47.560 --> 00:03:50.180
not interfere with any of the replacement pairs,
00:03:50.280 --> 00:03:53.380
and two, might slow down the search if they're overly long.
00:03:53.480 --> 00:03:54.820
Can we do better?
NOTE Solution: Single-pass
00:03:55.920 --> 00:03:56.740
Yes we can!
00:03:56.840 --> 00:03:59.580
We can actually perform just a single pass.
00:03:59.680 --> 00:04:02.180
The trick is to alternate between the replacement
00:04:02.280 --> 00:04:05.900
pairs, replacing whichever source occurs the earliest,
00:04:06.000 --> 00:04:08.340
and making sure to continue scanning after the end
00:04:08.440 --> 00:04:12.180
of the substituted target in order to avoid interference.
00:04:12.280 --> 00:04:14.420
This interleaving of replacements is not something
00:04:14.520 --> 00:04:17.140
that's easy to do by hand with query-replace.
NOTE Solution: Existing
00:04:18.240 --> 00:04:20.860
Since this is Emacs we're talking about, of course
00:04:20.960 --> 00:04:23.460
there already exist solutions that implement this idea.
00:04:23.560 --> 00:04:25.860
Here are few that we could find.
00:04:25.960 --> 00:04:28.700
The EmacsWiki has a page dedicated to this problem.
00:04:28.800 --> 00:04:31.460
Stack Overflow has an old post where a couple of
00:04:31.560 --> 00:04:33.860
users provided their solutions.
00:04:33.960 --> 00:04:36.820
Mastering Emacs also gives a method along with other
00:04:36.920 --> 00:04:38.900
interesting query-replace-regexp (C-M-%) patterns.
00:04:39.000 --> 00:04:42.260
More recently, Tony Zorman made a blogpost providing
00:04:42.360 --> 00:04:44.980
a solution with an interface based on query-replace.
00:04:45.080 --> 00:04:47.540
I encourage you to take a look at these solutions if
00:04:47.640 --> 00:04:48.940
you're interested in the details.
00:04:50.040 --> 00:04:52.940
But while a step in the right direction, these solutions
00:04:53.040 --> 00:04:55.340
are not satisfactory because they all lack one or
00:04:55.440 --> 00:04:56.820
more of the following.
00:04:56.920 --> 00:04:59.900
One, they are not completely automated and require
00:05:00.000 --> 00:05:02.500
the user to come up with a relatively complicated
00:05:02.600 --> 00:05:05.380
and verbose query-replace-regexp invocation.
00:05:06.080 --> 00:05:08.940
Two, they are restricted to performing only 2-element
00:05:09.040 --> 00:05:11.780
swaps rather than general parallel replacements.
00:05:12.680 --> 00:05:15.060
Three, they don't provide any sort of interactivity
00:05:15.160 --> 00:05:17.820
during replacement and instead perform it in one shot.
00:05:18.620 --> 00:05:21.300
Four, they don't attempt to integrate with the familiar
00:05:21.400 --> 00:05:24.900
query-replace interface, which supports skipping, undo,
00:05:25.000 --> 00:05:28.340
history and more advanced features like Lisp expressions
00:05:28.440 --> 00:05:29.900
and recursive query edits.
00:05:30.700 --> 00:05:33.700
Most importantly however, five, none of them were
00:05:33.800 --> 00:05:36.380
designed with regular expressions in mind and instead
00:05:36.480 --> 00:05:38.460
only ever consider literal strings.
00:05:39.560 --> 00:05:43.060
In fact, the only one that comes close is the
00:05:43.160 --> 00:05:46.420
half-automated solution that invokes query-replace-regexp
00:05:46.520 --> 00:05:48.100
with a specially crafted replacement.
00:05:48.800 --> 00:05:51.660
As an example, here's how you would use this technique
00:05:51.760 --> 00:05:54.340
to perform a 3-element parallel regex replacement.
00:05:54.440 --> 00:05:57.740
It uses the backslash-comma Lisp expression feature
00:05:57.840 --> 00:06:01.180
in order to choose the appropriate target to substitute.
00:06:01.280 --> 00:06:03.700
Aside from being very clumsy and tedious to write out,
00:06:03.800 --> 00:06:06.860
this approach makes it really hard to use more complex
00:06:06.960 --> 00:06:09.260
regular expressions that make use of capture groups
00:06:09.360 --> 00:06:10.600
themselves.
00:06:10.800 --> 00:06:12.200
This was the biggest limitation that we wanted
00:06:12.200 --> 00:06:15.020
to get rid of and the main motivation for our work.
00:06:15.720 --> 00:06:19.820
So, as an alternative to the existing zoo of 80% solutions,
00:06:19.920 --> 00:06:24.140
we aim to provide a 100% solution, one that handles
00:06:24.240 --> 00:06:27.020
regexes and consolidates all of the existing ideas
00:06:27.120 --> 00:06:28.180
into a single package.
NOTE Solution: query-replace-parallel
00:06:29.080 --> 00:06:31.260
We call it query-replace-parallel.
00:06:31.360 --> 00:06:34.060
The package is free and open-source and can currently
00:06:34.160 --> 00:06:37.300
be found on GitHub under hokomo/query-replace-parallel.
00:06:37.400 --> 00:06:40.140
The name is not yet finalized and we're open to any
00:06:40.240 --> 00:06:41.170
suggestions.
00:06:41.503 --> 00:06:43.180
We hope to get it published on an Elisp
00:06:43.280 --> 00:06:45.780
package archive in the near future, but for now you
00:06:45.880 --> 00:06:48.300
can just download and load the main Elisp file manually.
00:06:48.900 --> 00:06:51.300
With all of that said, let's go through a few demos
00:06:51.400 --> 00:06:54.140
to illustrate some use cases and see how to use the package.
NOTE Demonstration: Swap
00:06:55.240 --> 00:06:57.460
Our first demo is a simple swap, like the one we
00:06:57.560 --> 00:06:59.140
showed at the beginning of the presentation.
00:06:59.240 --> 00:07:02.060
This chunk of text is actually one of the tests
00:07:02.160 --> 00:07:03.140
from our package's code.
00:07:03.840 --> 00:07:06.420
Assuming we have loaded the package, we can execute
00:07:06.520 --> 00:07:09.580
the query-replace-parallel command, a parallel version
00:07:09.680 --> 00:07:11.220
of the standard query-replace.
00:07:11.320 --> 00:07:13.940
This command works with literal strings and will
00:07:14.040 --> 00:07:15.900
ask for each source and target in turn.
00:07:16.000 --> 00:07:21.260
Our goal is to replace "foo" with "bar"
00:07:21.360 --> 00:07:22.180
and "bar" with "foo".
00:07:24.680 --> 00:07:26.940
After inputting our replacements, we terminate the
00:07:27.040 --> 00:07:29.060
prompt by pressing enter with empty input.
00:07:29.860 --> 00:07:32.500
At this point, everything functions the same as in
00:07:32.600 --> 00:07:34.380
a standard query-replace invocation.
00:07:35.280 --> 00:07:37.300
The echo area shows the match and the replacement
00:07:37.400 --> 00:07:38.603
we're about to make.
00:07:38.703 --> 00:07:40.220
We can perform replacements,
00:07:43.920 --> 00:07:46.403
undo them,
00:07:46.503 --> 00:07:49.103
skip them,
00:07:49.203 --> 00:07:50.140
execute them until the end,
00:07:50.240 --> 00:07:51.020
and so on.
NOTE Demonstration: LaTeX
00:07:53.970 --> 00:07:56.180
The second demo shows our first regex use case.
00:07:56.280 --> 00:07:58.620
Imagine we have the following LaTeX code.
00:07:58.720 --> 00:08:01.380
We realize that we haven't been completely consistent
00:08:01.480 --> 00:08:03.940
in our use and naming of macros, so we decide to
00:08:04.040 --> 00:08:04.660
fix the problem.
00:08:05.536 --> 00:08:08.300
This time we execute query-replace-parallel-regexp
00:08:08.400 --> 00:08:10.900
because we want to work with regex instead of literal
00:08:11.000 --> 00:08:11.500
strings.
00:08:12.000 --> 00:08:13.420
We want to achieve two things.
00:08:13.520 --> 00:08:16.860
First, we want to wrap all usages of the variable n
00:08:16.960 --> 00:08:17.980
with the natvar macro.
00:08:18.080 --> 00:08:20.940
Using the backslash-less-than and blackslash-greater-than
00:08:21.040 --> 00:08:23.740
constructs allows us to only match letters n not
00:08:23.840 --> 00:08:25.260
appearing as part of a larger word.
00:08:25.360 --> 00:08:29.460
Second, we want to rename natvar to intvar because
00:08:29.560 --> 00:08:32.180
the variables a, b and c are integers and not natural
00:08:32.280 --> 00:08:32.700
numbers.
00:08:33.300 --> 00:08:35.660
We enter empty input to terminate the prompt and can
00:08:35.760 --> 00:08:37.180
now perform the replacements.
00:08:42.280 --> 00:08:44.380
There we go, the fixes are done and we didn't have
00:08:44.480 --> 00:08:46.100
to think about in which order to apply them.
NOTE Demonstration: Regex
00:08:48.700 --> 00:08:50.900
We now take a look at a more complicated regex
00:08:51.000 --> 00:08:53.580
example to demonstrate that even advanced query-replace
00:08:53.680 --> 00:08:54.300
features are supported.
00:08:55.100 --> 00:08:57.340
Each "foo" and "bar" in this example is followed by
00:08:57.440 --> 00:08:57.740
a number.
00:08:58.440 --> 00:09:01.280
The goal is to not only swap "foo" and "bar", but
00:09:01.380 --> 00:09:03.620
also increase or decrease the corresponding number.
00:09:03.720 --> 00:09:06.500
We first match "foo" and capture the number that
00:09:06.600 --> 00:09:07.100
follows it.
00:09:07.200 --> 00:09:09.900
For the target, we make use of the backslash-comma
00:09:10.000 --> 00:09:12.500
Lisp expression feature in order to replace the
00:09:12.600 --> 00:09:14.940
match with "bar" followed by the number's successor.
00:09:15.540 --> 00:09:17.540
We do the same thing for "bar", except that we
00:09:17.640 --> 00:09:19.140
replace the number with its predecessor.
00:09:27.040 --> 00:09:29.020
Performing the replacements, we can see how each
00:09:29.120 --> 00:09:31.220
number is incremented or decremented appropriately.
NOTE Demonstration: Order
00:09:36.320 --> 00:09:38.660
We haven't covered it explicitly so some of you may
00:09:38.760 --> 00:09:41.260
be wondering how parallel replacement deals with
00:09:41.360 --> 00:09:43.740
overlapping matches and whether the order of the
00:09:43.840 --> 00:09:45.380
replacement pairs is significant.
00:09:45.480 --> 00:09:47.860
This demo will clarify the exact behavior.
00:09:48.960 --> 00:09:51.700
The first example has the sources "watch" and "stopwatch".
00:09:57.500 --> 00:10:00.500
Conceptually, the matches overlap, but the rule is
00:10:00.600 --> 00:10:02.900
that matches are always processed earliest first,
00:10:03.000 --> 00:10:05.940
regardless of their length or the ordering of the pairs.
00:10:06.040 --> 00:10:08.980
Therefore it is "stopwatch" that gets replaced,
00:10:09.080 --> 00:10:10.940
and not its substring "watch".
00:10:16.040 --> 00:10:19.540
The second example uses the sources "watch" and "watchword".
00:10:19.640 --> 00:10:22.540
Both of the matches now conceptually start at the same
00:10:22.640 --> 00:10:23.020
position.
00:10:23.720 --> 00:10:26.300
In situations like these the order of the pairs does
00:10:26.400 --> 00:10:29.460
matter, and ties are broken by prefering the pair that
00:10:29.560 --> 00:10:32.180
was entered first, which is behavior that is inherited
00:10:32.280 --> 00:10:33.460
from the Elisp regex engine.
00:10:34.460 --> 00:10:37.380
So, the substring "watch" in "watchword" is what gets
00:10:37.480 --> 00:10:38.460
replaced in this case.
00:10:39.460 --> 00:10:41.740
Situations where the order of the pairs is significant
00:10:41.840 --> 00:10:44.740
are not very common however, so the user generally
00:10:44.840 --> 00:10:46.660
doesn't have to worry about this edge case.
00:10:46.760 --> 00:10:49.860
The order only matters when two or more sources
00:10:49.960 --> 00:10:52.340
share the same prefix, as in this example.
NOTE Demonstration: Fun
00:10:54.440 --> 00:10:56.860
The final demo tests the limits of the package and
00:10:56.960 --> 00:10:59.660
shows that it fully integrates with query-replace.
00:10:59.760 --> 00:11:02.940
It is really just for fun and can even serve as a
00:11:03.040 --> 00:11:04.140
small Emacs brainteaser.
00:11:04.240 --> 00:11:05.460
See if you can keep up!
00:11:06.360 --> 00:11:09.060
We open a directory and enter Writable Dired mode
00:11:09.160 --> 00:11:11.780
in order to rename the directories "foo" and "bar".
00:11:11.880 --> 00:11:14.660
Instead of doing it quickly by hand, we decide to
00:11:14.760 --> 00:11:17.260
show off and use query-replace-parallel-regexp.
00:11:17.360 --> 00:11:19.900
We enter our pairs and make use of the
00:11:20.000 --> 00:11:22.380
backslash-question-mark query edit feature.
00:11:25.080 --> 00:11:27.820
Now whenever we perform a replacement, the query
00:11:27.920 --> 00:11:30.740
edit makes Emacs stop and prompt us for additional
00:11:30.840 --> 00:11:32.180
input to use as the target.
00:11:36.680 --> 00:11:39.140
We confirm the renames and now enter the "bar-lib"
00:11:39.240 --> 00:11:41.900
directory in order to perform the same kind of
00:11:42.000 --> 00:11:43.900
replacement on "baz" and "quux".
00:11:44.500 --> 00:11:47.820
Rather than save time, we decide to be extra lazy
00:11:47.920 --> 00:11:48.820
and take the long route.
00:11:48.920 --> 00:11:52.220
We recall the first pair and initiate a recursive
00:11:52.320 --> 00:11:54.460
invocation of query-replace-parallel-regexp.
00:11:54.560 --> 00:11:57.020
We are now replacing the replacement.
00:12:01.020 --> 00:12:04.540
We apply our fixes and then do the same thing again
00:12:04.640 --> 00:12:05.870
with the second pair.
00:12:05.970 --> 00:12:07.500
Recall and recurse.
00:12:16.300 --> 00:12:19.860
We confirm the prompt and finally rename our directories.
00:12:25.360 --> 00:12:26.620
Wow, that really paid off.
NOTE Implementation
00:12:29.120 --> 00:12:31.380
Before we finish, a few quick words about the
00:12:31.480 --> 00:12:32.900
implementation for the curious.
00:12:33.300 --> 00:12:36.380
Both query-replace-parallel and query-replace-parallel-regexp
00:12:36.480 --> 00:12:39.140
delegate to the complex perform-replace function
00:12:39.240 --> 00:12:41.780
which is the workhorse of query-replace's interactive
00:12:41.880 --> 00:12:42.420
mechanism.
00:12:43.120 --> 00:12:45.420
The way we achieve multiple interleaved replacements
00:12:45.520 --> 00:12:49.020
is by providing perform-replace with a big "matcher regex"
00:12:49.120 --> 00:12:50.380
and a special replacement function.
00:12:50.480 --> 00:12:54.300
Essentially, a complex parallel replacement like this
00:12:54.400 --> 00:12:57.420
is transformed into a standard replacement like this.
00:12:57.520 --> 00:13:00.100
This is similar to the trick shown earlier in the
00:13:00.200 --> 00:13:00.780
presentation.
00:13:00.880 --> 00:13:03.820
Each source is put in its own capture group to allow
00:13:03.920 --> 00:13:06.340
the replacement function to determine which one matched
00:13:06.440 --> 00:13:08.380
and return the appropriate target.
00:13:08.980 --> 00:13:11.580
However, we now take care to support arbitrary
00:13:11.680 --> 00:13:13.380
regular expressions as sources.
00:13:13.480 --> 00:13:16.980
We achieve this by converting each source regex into
00:13:17.080 --> 00:13:19.820
an equivalent one for which we can guarantee that its
00:13:19.920 --> 00:13:22.820
capture groups will not clash with our matcher regex.
00:13:22.920 --> 00:13:25.900
Information about this conversion is stored, and
00:13:26.000 --> 00:13:28.220
once the replacement function is called it has
00:13:28.320 --> 00:13:30.260
enough data to apply the replacement from the
00:13:30.360 --> 00:13:32.020
viewpoint of the original regex.
00:13:32.720 --> 00:13:34.900
The regex transformation is reliable because it
00:13:35.000 --> 00:13:38.420
uses the rx library, allowing us to treat regexes
00:13:38.520 --> 00:13:41.940
as s-expressions and avoid any nasty manual parsing.
00:13:42.640 --> 00:13:46.540
In fact, rx itself is based on one of Olin Shivers'
00:13:46.640 --> 00:13:48.336
100% solutions:
00:13:48.436 --> 00:13:51.220
SRE, or the S-expression regex notation.
00:13:51.320 --> 00:13:54.340
We all stand on the shoulders of many giants, so
00:13:54.440 --> 00:13:56.500
let's strive to design good solutions that we can
00:13:56.600 --> 00:13:59.140
all benefit from, many years into the future!
00:13:59.240 --> 00:14:02.900
Finally, because query-replace's core is not completely
00:14:03.000 --> 00:14:06.060
customizable, we did have to sprinkle in some advice
00:14:06.160 --> 00:14:07.500
to get certain things working.
00:14:07.600 --> 00:14:11.060
This concerns only minor cosmetic fixes and not the
00:14:11.160 --> 00:14:13.940
core replacement functionality, but we have nontheless
00:14:14.040 --> 00:14:16.580
tried to do it in the simplest and least intrusive way
00:14:16.680 --> 00:14:17.140
possible.
NOTE End
00:14:18.740 --> 00:14:21.580
In conclusion, go download and play with the package.
00:14:21.680 --> 00:14:24.460
Even if you're not performing overlapping replacements,
00:14:24.560 --> 00:14:26.780
you can still use query-replace-parallel for the
00:14:26.880 --> 00:14:29.620
peace of mind knowing that things won't go wrong if
00:14:29.720 --> 00:14:31.860
you perform more than one replacement at a time.
00:14:32.460 --> 00:14:34.540
Feel free to let us know about any interesting or
00:14:34.640 --> 00:14:37.460
crazy use cases you might come up with, as well as
00:14:37.560 --> 00:14:40.540
improvements or bugs that make it only a 99% solution.
00:14:40.640 --> 00:14:45.560
Thanks for listening and have a great EmacsConf!