blob: c1390ced7f172728ce4ecb766b0fe4f91df3f5de (
plain)
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
|
<!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="© 2024 nsfisis">
<meta name="description" content="Zsh の Composer に対する補完はカスタムコマンドやその引数を補完しない。カスタムコマンドの引数としてファイルを補完させる方法を調べた。">
<meta name="keywords" content="Composer,PHP,Zsh">
<meta property="og:type" content="article">
<meta property="og:title" content="【Zsh】 Composer のカスタムコマンドに対する Zsh 補完で引数にファイルを補完させる|REPL: Rest-Eat-Program Loop">
<meta property="og:description" content="Zsh の Composer に対する補完はカスタムコマンドやその引数を補完しない。カスタムコマンドの引数としてファイルを補完させる方法を調べた。">
<meta property="og:site_name" content="REPL: Rest-Eat-Program Loop">
<meta property="og:locale" content="ja_JP">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<title>【Zsh】 Composer のカスタムコマンドに対する Zsh 補完で引数にファイルを補完させる|REPL: Rest-Eat-Program Loop</title>
<link rel="stylesheet" href="/style.css?h=60eb349e583f5bd51518a7eb98598043">
</head>
<body class="single">
<header class="header">
<div class="site-logo">
<a href="/">REPL: Rest-Eat-Program Loop</a>
</div>
<nav class="nav">
<ul>
<li>
<a href="/about/">About</a>
</li>
<li>
<a href="/posts/">Posts</a>
</li>
<li>
<a href="/slides/">Slides</a>
</li>
<li>
<a href="/tags/">Tags</a>
</li>
</ul>
</nav>
</header>
<main class="main">
<article class="post-single">
<header class="post-header">
<h1 class="post-title">【Zsh】 Composer のカスタムコマンドに対する Zsh 補完で引数にファイルを補完させる</h1>
<ul class="post-tags">
<li class="tag">
<a href="/tags/composer/">Composer</a>
</li>
<li class="tag">
<a href="/tags/php/">PHP</a>
</li>
<li class="tag">
<a href="/tags/zsh/">Zsh</a>
</li>
</ul>
</header>
<div class="post-content">
<section>
<h2 id="changelog">更新履歴</h2>
<ol>
<li class="revision">
<time datetime="2024-04-29">2024-04-29</time>: 公開
</li>
</ol>
</section>
<section id="section--version-info">
<h2><a href="#section--version-info">バージョン情報</a></h2>
<ul>
<li>
Composer: 2.7.4
</li>
<li>
PHP: 8.3.6
</li>
<li>
Zsh: 5.9
</li>
</ul>
</section>
<section id="section--intro">
<h2><a href="#section--intro">はじめに</a></h2>
<p>
<a href="https://getcomposer.org/" rel="noreferrer" target="_blank">Composer</a> は PHP のデファクトスタンダードなパッケージマネージャである。Zsh では、<code>composer</code> コマンドに対する補完が提供されており、<code>composer</code> と入力してタブキーを押すと、利用可能なコマンドやオプションが補完される。Zsh の補完はシェル関数の形で実装されており、<code>composer</code> コマンドに対応した補完をおこなうのは <code>_composer</code> である。<a href="https://github.com/zsh-users/zsh/blob/a66e92918568881af110a3e2e3018b317c054e4a/Completion/Unix/Command/_composer" rel="noreferrer" target="_blank">記事執筆時点での補完関数の定義は、GitHub のミラーリポジトリから参照できる。</a>
</p>
</section>
<section id="section--problem">
<h2><a href="#section--problem">発生していた問題</a></h2>
<p>
<code>composer</code> コマンドはカスタムコマンド (<code>composer.json</code> の <code>scripts</code> で定義されたコマンド) に対して補完をおこなわない。つまり、途中まで入力されたカスタムコマンドを補完しないし、カスタムコマンドの引数も補完しない。例えば、PHPUnit を呼び出す <code>phpunit</code> というカスタムコマンドを定義し <code>composer phpu</code> まで打ってタブキーを押しても、<code>composer phpunit</code> にはならない。また、<code>composer phpunit -- --</code> まで打ってタブキーを押しても、<code>phpunit</code> コマンドのオプションは補完されない。
</p>
<p>
このことは、先ほどリンクを載せた <code>_composer</code> 関数を定義しているファイルの冒頭にも書かれている。
</p>
<div class="codeblock" language="zsh">
<pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#6A737D"># - @todo We don't complete custom commands (including script aliases). This is</span></span>
<span class="line"><span style="color:#6A737D"># easy to do in the general case, but it probably requires some clever caching</span></span>
<span class="line"><span style="color:#6A737D"># to avoid introducing a noticeable lag to every completion operation, due to</span></span>
<span class="line"><span style="color:#6A737D"># the way command resolution works and the fact that discovering custom</span></span>
<span class="line"><span style="color:#6A737D"># commands requires making slow calls to Composer</span></span></code></pre>
</div>
</section>
<section id="section--what-i-want-to-achive">
<h2><a href="#section--what-i-want-to-achive">やりたいこと</a></h2>
<p>
確かに、カスタムコマンドに対して完全な補完を提供するのは不可能か、あるいは実現できても遅くなりすぎるだろう。しかし、不完全なフォールバックを提供するくらいなら可能なはずだ。
</p>
<p>
この記事では、これらのカスタムコマンドについて、Zsh が提供するデフォルトのファイル・ディレクトリ補完を適用する。つまり、<code>composer phpunit -- tests/</code> まで打ってタブキーを押すと、<code>tests</code> ディレクトリの下にあるテストファイルまたはディレクトリが補完される。
</p>
</section>
<section id="section--solution">
<h2><a href="#section--solution">解決策</a></h2>
<p>
まずは、Zsh で補完関数を提供する場合のボイラープレートコードを書く。以下は <code>~/.zshrc</code> にすべて書く前提だが、<code>autoload</code> を設定するなどすれば別ファイルに分離できる (詳細な手順は割愛)。
</p>
<div class="codeblock" language="zsh">
<pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#6F42C1">compdef</span><span style="color:#032F62"> _my_composer</span><span style="color:#032F62"> composer</span><span style="color:#032F62"> composer.phar</span></span></code></pre>
</div>
<p>
<code>compdef</code> は Zsh が用意している関数で、第一引数に補完関数の名前、第二引数以降に補完を適用するコマンド名を並べる。この場合は、<code>composer</code> コマンドや <code>composer.phar</code> コマンドに対して <code>_my_composer</code> を使って補完をおこなうよう定義している。
</p>
<p>
次に <code>_my_composer</code> を定義する。基本的にはデフォルトの <code>composer</code> コマンドの補完関数 (つまり <code>_composer</code> 関数) を使い、それが何も返さなかった場合に限り、Zsh のファイル・ディレクトリ補完へフォールバックする。
</p>
<div class="codeblock" language="zsh">
<pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">function</span><span style="color:#6F42C1"> _my_composer</span><span style="color:#24292E">() {</span></span>
<span class="line"><span style="color:#6F42C1"> _composer</span><span style="color:#032F62"> "</span><span style="color:#005CC5">$@</span><span style="color:#032F62">"</span><span style="color:#D73A49"> ||</span><span style="color:#6F42C1"> _files</span><span style="color:#032F62"> "</span><span style="color:#005CC5">$@</span><span style="color:#032F62">"</span></span>
<span class="line"><span style="color:#24292E">}</span></span></code></pre>
</div>
<p>
<code>_composer</code> コマンドは何も補完候補がなかったとき非ゼロな exit status で終了するので、そうであったなら <code>_files</code> を呼び出す。<code>_files</code> は、Zsh がデフォルトで用意しているファイル・ディレクトリの補完をおこなう関数である。
</p>
</section>
<section id="section--conclusion">
<h2><a href="#section--conclusion">まとめ</a></h2>
<p>
これらの設定をおこなうことで、部分的ながら Composer のカスタムコマンドに対して補完をおこなうことができる。特に、PHPUnit や PHPStan などの対象ファイル・ディレクトリを引数に取るようなコマンドを使う場合に有用であろう。
</p>
</section>
</div>
</article>
</main>
<footer class="footer">
© 2021 nsfisis
</footer>
</body>
</html>
|