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
|
<!DOCTYPE html>
<html lang="ja-JP">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="author" content="nsfisis">
<meta name="copyright" content="© nsfisis">
<meta name="description" content="Vim で選択した行の順番を入れ替える方法。">
<meta name="keywords" content="Vim">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<title>Vimで選択した行の順番を入れ替える | REPL: Rest-Eat-Program Loop</title>
<link rel="stylesheet" href="/hl.css?208c52e3b7c9db1cad782c5d30b4698f">
<link rel="stylesheet" href="/style.css?779b1a3debcaeba619f6fe500e93d525">
<link rel="stylesheet" href="/custom.css?a649ea3528d4b626fb636505d94c1144">
</head>
<body class="single">
<header class="header">
<nav class="nav">
<p class="logo">
<a href="/">REPL: Rest-Eat-Program Loop</a>
</p>
</nav>
</header>
<main class="main">
<article class="post-single">
<header class="post-header">
<h1 class="post-title">Vimで選択した行の順番を入れ替える</h1>
<ul class="post-tags">
<li class="tag">
<a href="/tags/vim/">Vim</a>
</li>
</ul>
</header>
<div class="post-content">
<section>
<h2 id="changelog">更新履歴</h2>
<ol>
<li class="revision">
<time datetime="2021-10-02">2021-10-02</time>: Qiita から移植
</li>
</ol>
</section>
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>この記事は Qiita から移植してきたものです。 元 URL:
<a href="https://qiita.com/nsfisis/items/4fefb361d9a693803520" class="bare">https://qiita.com/nsfisis/items/4fefb361d9a693803520</a></p>
</div>
<hr>
</div>
</div>
<section class="section-1">
<h2 id="" class="section-header">
バージョン情報
</h2>
<div class="section-body">
<div class="paragraph">
<p><code>:version</code> の一部</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Jan 26 2020 11:30:30) macOS
version Included patches: 1-148 Huge version without GUI.</p>
</div>
</blockquote>
</div>
</div>
</section>
<section class="section-1">
<h2 id="" class="section-header">
よく紹介されている手法
</h2>
<div class="section-body">
<section class="section-2">
<h3 id="" class="section-header">
<code>tac</code> / <code>tail</code>
</h3>
<div class="section-body">
<div class="paragraph">
<p><code>tac</code> や <code>tail -r</code> などの外部コマンドを <code>!</code>
を使って呼び出し、置き換える。</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>:h v_!</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p><code>tac</code> コマンドや <code>tail</code> の <code>-r</code>
オプションは環境によって利用できないことがあり、複数の環境を行き来する場合に採用しづらい</p>
</div>
</div>
</section>
<section class="section-2">
<h3 id="" class="section-header">
<code>:g/^/m0</code>
</h3>
<div class="section-body">
<div class="paragraph">
<p>こちらは外部コマンドに頼らず、Vim の機能のみを使う。<code>g</code> は <code>:global</code>
コマンドの、<code>m</code> は <code>:move</code> コマンドの略</p>
</div>
<div class="paragraph">
<p><code>:global</code> コマンドは <code>:[range]global/{pattern}/[command]</code>
のように使い、<code>[range]</code> で指定された範囲の行のうち、<code>{pattern}</code>
で指定された検索パターンにマッチする行に対して、順番に <code>[command]</code>
で指定された Ex コマンドを呼び出す。</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>:h :global</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p><code>:move</code> コマンドは <code>[range]:move {address}</code> のように使い、<code>[range]</code>
で指定された範囲の行を <code>{address}</code> で指定された位置に移動させる。</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>:h :move</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p><code>:g/^/m0</code> のように組み合わせると、「すべての行を1行ずつ
0行目(1行目の上)に動かす」という動きをする。これは確かに行の入れ替えになっている。</p>
</div>
<div class="paragraph">
<p>なお、<code>:g/^/m0</code> は全ての行を入れ替えるが、<code>:N,Mg/^/mN-1</code> とすることで
N行目から
M行目を処理範囲とするよう拡張できる。手でこれを入力するわけにはいかないので、次のようなコマンドを用意する。</p>
</div>
<div id="source." class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="vim">command<span class="p">!</span> <span class="p">-</span>bar <span class="p">-</span><span class="nb">range</span><span class="p">=</span>%
<span class="se"> \</span> Reverse
<span class="se"> \</span> <span class="p"><</span>line1<span class="p">>,<</span>line2<span class="p">></span><span class="k">g</span><span class="sr">/^/</span><span class="k">m</span><span class="p"><</span>line1<span class="p">></span><span class="m">-1</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>これは望みの動作をするが、実際に実行してみると全行がハイライトされてしまう。次節で詳細を述べる。</p>
</div>
</div>
</section>
</div>
</section>
<section class="section-1">
<h2 id="" class="section-header">
<code>:g/^/m0</code> の問題点
</h2>
<div class="section-body">
<div class="paragraph">
<p><code>:global</code>
コマンドは各行に対してマッチングを行う際、現在の検索パターンを上書きしてしまう。<code>^</code>
は行の先頭にマッチするため、結果として全ての行がハイライトされてしまう。<code>'hlsearch'</code>
オプションを無効にしている場合その限りではないが、その場合でも直前の検索パターンが失われてしまうと
<code>n</code> コマンドなどの際に不便である。</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>:h @/</p>
</div>
</blockquote>
</div>
</div>
</section>
<section class="section-1">
<h2 id="" class="section-header">
解決策
</h2>
<div class="section-body">
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>[2020/9/28追記] より簡潔な方法を見つけたので次節に追記した</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>前述した <code>:Reverse</code> コマンドの定義を少し変えて、次のようにする:</p>
</div>
<div id="source." class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="vim"><span class="k">function</span><span class="p">!</span> <span class="nv">s:reverse_lines</span><span class="p">(</span>from<span class="p">,</span> <span class="k">to</span><span class="p">)</span> abort
<span class="nb">execute</span> <span class="nb">printf</span><span class="p">(</span><span class="s2">"%d,%dg/^/m%d"</span><span class="p">,</span> <span class="nv">a:from</span><span class="p">,</span> <span class="nv">a:to</span><span class="p">,</span> <span class="nv">a:from</span> <span class="p">-</span> <span class="m">1</span><span class="p">)</span>
<span class="k">endfunction</span>
command<span class="p">!</span> <span class="p">-</span>bar <span class="p">-</span><span class="nb">range</span><span class="p">=</span>%
<span class="se"> \</span> Reverse
<span class="se"> \</span> <span class="k">call</span> <span class="p"><</span>SID<span class="p">></span>reverse_lines<span class="p">(<</span>line1<span class="p">>,</span> <span class="p"><</span>line2<span class="p">>)</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>実行しているコマンドが変わったわけではないが、関数呼び出しを経由するようにした。これだけで前述の問題が解決する。</p>
</div>
<div class="paragraph">
<p>この理由は、ユーザー定義関数を実行する際は検索パターンが一度保存され、実行が終了したあと復元されるため。結果として検索パターンが
<code>^</code> で上書きされることがなくなる。</p>
</div>
<div class="paragraph">
<p>Vim のヘルプから該当箇所を引用する (強調は筆者による)。</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>:h autocmd-searchpat</p>
</div>
<div class="paragraph">
<p><strong>Autocommands do not change the current search patterns.</strong> Vim saves the
current search patterns before executing autocommands then restores them
after the autocommands finish. This means that autocommands do not
affect the strings highlighted with the `hlsearch' option.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>これは autocommand
の実行に関しての記述だが、これと同じことがユーザー定義関数の実行時にも適用される。このことは
<code>:nohlsearch</code> のヘルプにある。同じく該当箇所を引用する
(強調は筆者による)。</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>:h :nohlsearch</p>
</div>
<div class="paragraph">
<p>(略) This command doesn’t work in an autocommand, because the
highlighting state is saved and restored when executing autocommands
|autocmd-searchpat|. <strong>Same thing for when invoking a user function.</strong></p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>この仕様により、<code>:g/^/m0</code>
の呼び出しをユーザー定義関数に切り出すことで上述の問題を解決できる。</p>
</div>
</div>
</section>
<section class="section-1">
<h2 id="" class="section-header">
解決策 (改訂版)
</h2>
<div class="section-body">
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>[2020/9/28追記] より簡潔な方法を見つけたため追記する</p>
</div>
</blockquote>
</div>
<div id="source." class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="vim">command<span class="p">!</span> <span class="p">-</span>bar <span class="p">-</span><span class="nb">range</span><span class="p">=</span>%
<span class="se"> \</span> Reverse
<span class="se"> \</span> <span class="k">keeppatterns</span> <span class="p"><</span>line1<span class="p">>,<</span>line2<span class="p">></span><span class="k">g</span><span class="sr">/^/</span><span class="k">m</span><span class="p"><</span>line1<span class="p">></span><span class="m">-1</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>まさにこのための Exコマンド、<code>:keeppatterns</code>
が存在する。<code>:keeppatterns {command}</code>
のように使い、読んで字の如く、後ろに続く
Exコマンドを「現在の検索パターンを保ったまま」実行する。はるかに分かりやすく意図を表現できる。</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>:h :keeppatterns</p>
</div>
</blockquote>
</div>
</div>
</section>
<section class="section-1">
<h2 id="" class="section-header">
コピペ用再掲
</h2>
<div class="section-body">
<div id="source." class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="vim"><span class="c">" License: Public Domain</span>
command<span class="p">!</span> <span class="p">-</span>bar <span class="p">-</span><span class="nb">range</span><span class="p">=</span>%
<span class="se"> \</span> Reverse
<span class="se"> \</span> <span class="k">keeppatterns</span> <span class="p"><</span>line1<span class="p">>,<</span>line2<span class="p">></span><span class="k">g</span><span class="sr">/^/</span><span class="k">m</span><span class="p"><</span>line1<span class="p">></span><span class="m">-1</span></code></pre>
</div>
</div>
</div>
</section>
</div>
</article>
</main>
<footer class="footer">
© 2021 nsfisis
</footer>
</body>
</html>
|