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
|
<!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="Neovim で空の PHP ファイルを開いたとき、ディレクトリの構造に基づいて自動的に namespace 宣言を挿入するようにする。">
<meta name="keywords" content="Neovim,PHP">
<meta property="og:type" content="article">
<meta property="og:title" content="【Neovim】 空の PHP ファイルに namespace 宣言を挿入する|REPL: Rest-Eat-Program Loop">
<meta property="og:description" content="Neovim で空の PHP ファイルを開いたとき、ディレクトリの構造に基づいて自動的に namespace 宣言を挿入するようにする。">
<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>【Neovim】 空の PHP ファイルに namespace 宣言を挿入する|REPL: Rest-Eat-Program Loop</title>
<link rel="stylesheet" href="/style.css?h=99f9dcb3410627bbc92c4985b4b59520">
</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">【Neovim】 空の PHP ファイルに namespace 宣言を挿入する</h1>
<ul class="post-tags">
<li class="tag">
<a href="/tags/neovim/">Neovim</a>
</li>
<li class="tag">
<a href="/tags/php/">PHP</a>
</li>
</ul>
</header>
<div class="post-content">
<section id="changelog">
<h2><a href="#changelog">更新履歴</a></h2>
<ol>
<li class="revision">
<time datetime="2024-01-10">2024-01-10</time>: 公開
</li>
</ol>
</section>
<div class="admonition">
<div class="admonition-label">
NOTE
</div>
<div class="admonition-content">
<p>
この記事は <a href="https://vim-jp.org/ekiden/" rel="noreferrer" target="_blank">Vim 駅伝</a> #136 の記事です。
</p>
</div>
</div>
<section id="section--intro">
<h2><a href="#section--intro">やりたいこと</a></h2>
<p>
Neovim で空の PHP ファイルを開いたとき、そのファイルが置かれているディレクトリの構造に基づいて、自動的に <code>namespace</code> 宣言を挿入したい。具体的には、トップレベルの名前空間が <code>MyNamespace</code> であり、ファイル <code>src/Foo/Bar/Baz.php</code> を開いたときに、そのファイルが空であるなら、次のようなテンプレートが自動的に挿入されてほしい。
</p>
<div class="codeblock">
<pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49"><?</span><span style="color:#005CC5">php</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">namespace</span><span style="color:#6F42C1"> MyNamespace\Foo\Bar</span><span style="color:#24292E">;</span></span></code></pre>
</div>
</section>
<section id="section--version">
<h2><a href="#section--version">バージョン情報</a></h2>
<div class="codeblock">
<pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span>$ nvim --version</span></span>
<span class="line"><span>NVIM v0.9.2</span></span>
<span class="line"><span>Build type: Release</span></span>
<span class="line"><span>LuaJIT 2.1.1693350652</span></span></code></pre>
</div>
<p>
今回は Lua で処理を記述したため、Vim では動作しない。以下の説明でも Neovim に絞って述べる。また、パス区切りがスラッシュである前提で記述したため、Windows には対応していない。
</p>
</section>
<section id="section--ftplugin">
<h2><a href="#section--ftplugin">ftplugin を用意する</a></h2>
<p>
Neovim には特定のファイルタイプに対して特別な処理をおこなうための ftplugin と呼ばれる仕組みがある。Neovim の設定を置くディレクトリ (例えば <code>~/.config/nvim</code>) の配下に <code>ftplugin/<FILE_TYPE>.vim</code> または <code>ftplugin/<FILE_TYPE>.lua</code> というファイルを配置すると、その <code><FILE_TYPE></code> が読み込まれたときにそのファイルが自動的に実行される。
</p>
<p>
今回は、Neovim がデフォルトで用意している PHP 用 ftplugin が動作したあとに追加の処理をおこないたいので、<code>after/ftplugin/php.{vim,lua}</code> というファイルを配置する。名前から察せられるとおり、<code>after/ftplugin</code> 以下のファイルは <code>ftplugin</code> 以下のファイルよりもあとに実行される。
</p>
<p>
この記事では Lua で処理を記述するため、拡張子には <code>.lua</code> を用いる。これ以降載せるコードは、すべて <code>after/ftplugin/php.lua</code> の中に記述している。
</p>
</section>
<section id="section--did-ftplugin">
<h2><a href="#section--did-ftplugin">二重読み込みを防ぐ</a></h2>
<p>
ファイルタイプは読み込んだあとに変更されることもあるので、ftplugin は複数回実行されうる。二重読み込みを防ぐために、<code>did_ftplugin_<FILE_TYPE>_after</code> というバッファローカル変数を定義しておくのが慣習となっている。
</p>
<div class="codeblock">
<pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> vim.</span><span style="color:#6F42C1">b</span><span style="color:#24292E">.</span><span style="color:#6F42C1">did_ftplugin_php_after</span><span style="color:#D73A49"> then</span></span>
<span class="line"><span style="color:#D73A49"> return</span></span>
<span class="line"><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">-- ここに実際の処理を書く</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">vim.</span><span style="color:#6F42C1">b</span><span style="color:#24292E">.</span><span style="color:#6F42C1">did_ftplugin_php_after</span><span style="color:#D73A49"> =</span><span style="color:#005CC5"> true</span></span></code></pre>
</div>
</section>
<section id="section--implement">
<h2><a href="#section--implement">実装する</a></h2>
<p>
では実装していこう。今回私は次のようなロジックとした。以降、「今 Neovim で開いた PHP ファイル」のことを「対象ファイル」と呼ぶことにする。
</p>
<ol>
<li>
対象ファイルが空でなければ何もしない
</li>
<li>
対象ファイルが置かれたディレクトリを上に辿って、<code>composer.json</code> を見つける
</li>
<li>
<code>composer.json</code> の <code>autoload.psr-4</code> を見て、トップレベルの名前空間とディレクトリを特定する
</li>
<li>
対象ファイルが置かれたディレクトリが、トップレベルのディレクトリを基準としてどのようにネストしているか調べる
</li>
<li>
オートロードの設定と照らし合わせて、対象ファイルが属すべき名前空間を特定する
</li>
<li>
PHP の開始タグとともに <code>namespace</code> 宣言を挿入する
</li>
</ol>
<p>
実装を簡単にするため、Composer を用いない場合や PSR 4 以外のオートロード規則を使う場合には対応しない。少々長くなるが、以下にスクリプト全文を載せる。
</p>
<div class="codeblock">
<pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> vim.</span><span style="color:#6F42C1">b</span><span style="color:#24292E">.</span><span style="color:#6F42C1">did_ftplugin_php_after</span><span style="color:#D73A49"> then</span></span>
<span class="line"><span style="color:#D73A49"> return</span></span>
<span class="line"><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">-- base_dir を起点としてディレクトリを上向きに辿っていき、composer.json を探す</span></span>
<span class="line"><span style="color:#6A737D">-- :help vim.fs.find()</span></span>
<span class="line"><span style="color:#D73A49">local</span><span style="color:#D73A49"> function</span><span style="color:#6F42C1"> find_composer_json</span><span style="color:#24292E">(base_dir)</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#24292E"> vim.</span><span style="color:#6F42C1">fs</span><span style="color:#24292E">.</span><span style="color:#005CC5">find</span><span style="color:#24292E">(</span><span style="color:#032F62">'composer.json'</span><span style="color:#24292E">, {</span></span>
<span class="line"><span style="color:#24292E"> path </span><span style="color:#D73A49">=</span><span style="color:#24292E"> base_dir,</span></span>
<span class="line"><span style="color:#24292E"> upward </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#6A737D"> -- ホームディレクトリまで到達したら探索を打ち切る</span></span>
<span class="line"><span style="color:#24292E"> stop </span><span style="color:#D73A49">=</span><span style="color:#24292E"> vim.</span><span style="color:#6F42C1">loop</span><span style="color:#24292E">.</span><span style="color:#005CC5">os_homedir</span><span style="color:#24292E">(),</span></span>
<span class="line"><span style="color:#24292E"> type </span><span style="color:#D73A49">=</span><span style="color:#032F62"> 'file'</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#24292E"> })[</span><span style="color:#005CC5">1</span><span style="color:#24292E">]</span></span>
<span class="line"><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">-- JSON ファイルを読み込み、デコードして返す</span></span>
<span class="line"><span style="color:#6A737D">-- :help readblob()</span></span>
<span class="line"><span style="color:#6A737D">-- :help vim.json.decode</span></span>
<span class="line"><span style="color:#6A737D">-- :help luaref-pcall()</span></span>
<span class="line"><span style="color:#D73A49">local</span><span style="color:#D73A49"> function</span><span style="color:#6F42C1"> load_json</span><span style="color:#24292E">(file_path)</span></span>
<span class="line"><span style="color:#6A737D"> -- readblob() は Vim script では Blob オブジェクトを返すが、Lua から呼ぶと string に変換される</span></span>
<span class="line"><span style="color:#D73A49"> local</span><span style="color:#24292E"> ok_read, content </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> pcall</span><span style="color:#24292E">(vim.</span><span style="color:#6F42C1">fn</span><span style="color:#24292E">.</span><span style="color:#6F42C1">readblob</span><span style="color:#24292E">, file_path)</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#D73A49"> not</span><span style="color:#24292E"> ok_read </span><span style="color:#D73A49">then</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#005CC5"> nil</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"><span style="color:#D73A49"> local</span><span style="color:#24292E"> ok_decode, obj </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> pcall</span><span style="color:#24292E">(vim.</span><span style="color:#6F42C1">json</span><span style="color:#24292E">.</span><span style="color:#6F42C1">decode</span><span style="color:#24292E">, content)</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#D73A49"> not</span><span style="color:#24292E"> ok_decode </span><span style="color:#D73A49">then</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#005CC5"> nil</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#24292E"> obj</span></span>
<span class="line"><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">-- 対象ファイルの置かれたディレクトリを基に namespace 宣言を生成する</span></span>
<span class="line"><span style="color:#6A737D">-- :help nvim_buf_get_name()</span></span>
<span class="line"><span style="color:#6A737D">-- :help vim.fs.dirname()</span></span>
<span class="line"><span style="color:#D73A49">local</span><span style="color:#D73A49"> function</span><span style="color:#6F42C1"> generate_namespace_declaration</span><span style="color:#24292E">()</span></span>
<span class="line"><span style="color:#6A737D"> -- composer.json を探し、トップレベルの名前空間とディレクトリを特定する</span></span>
<span class="line"><span style="color:#D73A49"> local</span><span style="color:#24292E"> current_dir </span><span style="color:#D73A49">=</span><span style="color:#24292E"> vim.</span><span style="color:#6F42C1">fs</span><span style="color:#24292E">.</span><span style="color:#005CC5">dirname</span><span style="color:#24292E">(vim.</span><span style="color:#6F42C1">api</span><span style="color:#24292E">.</span><span style="color:#005CC5">nvim_buf_get_name</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">))</span></span>
<span class="line"><span style="color:#D73A49"> local</span><span style="color:#24292E"> path_to_composer_json </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> find_composer_json</span><span style="color:#24292E">(current_dir)</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#D73A49"> not</span><span style="color:#24292E"> path_to_composer_json </span><span style="color:#D73A49">then</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#005CC5"> nil</span><span style="color:#6A737D"> -- failed to locate composer.json</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"><span style="color:#D73A49"> local</span><span style="color:#24292E"> composer_json </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> load_json</span><span style="color:#24292E">(path_to_composer_json)</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#D73A49"> not</span><span style="color:#24292E"> composer_json </span><span style="color:#D73A49">then</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#005CC5"> nil</span><span style="color:#6A737D"> -- failed to load composer.json</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"><span style="color:#6A737D"> -- autoload.psr-4 を探し、型が期待される型と一致するかどうか調べる</span></span>
<span class="line"><span style="color:#D73A49"> local</span><span style="color:#24292E"> psr4 </span><span style="color:#D73A49">=</span><span style="color:#24292E"> vim.</span><span style="color:#005CC5">tbl_get</span><span style="color:#24292E">(composer_json, </span><span style="color:#032F62">'autoload'</span><span style="color:#24292E">, </span><span style="color:#032F62">'psr-4'</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#D73A49"> not</span><span style="color:#24292E"> psr4 </span><span style="color:#D73A49">then</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#005CC5"> nil</span><span style="color:#6A737D"> -- autoload.psr-4 section is absent</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> vim.</span><span style="color:#005CC5">tbl_count</span><span style="color:#24292E">(psr4) </span><span style="color:#D73A49">~=</span><span style="color:#005CC5"> 1</span><span style="color:#D73A49"> then</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#005CC5"> nil</span><span style="color:#6A737D"> -- psr-4 section is ambiguous</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"><span style="color:#D73A49"> local</span><span style="color:#24292E"> psr4_namespace, psr4_dir</span></span>
<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> k, v </span><span style="color:#D73A49">in</span><span style="color:#005CC5"> pairs</span><span style="color:#24292E">(psr4) </span><span style="color:#D73A49">do</span></span>
<span class="line"><span style="color:#24292E"> psr4_namespace </span><span style="color:#D73A49">=</span><span style="color:#24292E"> k</span></span>
<span class="line"><span style="color:#24292E"> psr4_dir </span><span style="color:#D73A49">=</span><span style="color:#24292E"> v</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#005CC5"> type</span><span style="color:#24292E">(psr4_dir) </span><span style="color:#D73A49">==</span><span style="color:#032F62"> 'table' </span><span style="color:#D73A49">then</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#D73A49"> #</span><span style="color:#24292E">psr4_dir </span><span style="color:#D73A49">==</span><span style="color:#005CC5"> 1</span><span style="color:#D73A49"> then</span></span>
<span class="line"><span style="color:#24292E"> psr4_dir </span><span style="color:#D73A49">=</span><span style="color:#24292E"> psr4_dir[</span><span style="color:#005CC5">1</span><span style="color:#24292E">]</span></span>
<span class="line"><span style="color:#D73A49"> else</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#005CC5"> nil</span><span style="color:#6A737D"> -- psr-4 section is ambiguous</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#005CC5"> type</span><span style="color:#24292E">(psr4_namespace) </span><span style="color:#D73A49">~=</span><span style="color:#032F62"> 'string' </span><span style="color:#D73A49">or</span><span style="color:#005CC5"> type</span><span style="color:#24292E">(psr4_dir) </span><span style="color:#D73A49">~=</span><span style="color:#032F62"> 'string' </span><span style="color:#D73A49">then</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#005CC5"> nil</span><span style="color:#6A737D"> -- psr-4 section is invalid</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"><span style="color:#6A737D"> -- 末尾のスラッシュとバックスラッシュを取り除いておく</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#6F42C1"> psr4_namespace</span><span style="color:#24292E">:</span><span style="color:#005CC5">sub</span><span style="color:#24292E">(</span><span style="color:#D73A49">-</span><span style="color:#005CC5">1</span><span style="color:#24292E">, </span><span style="color:#D73A49">-</span><span style="color:#005CC5">1</span><span style="color:#24292E">) </span><span style="color:#D73A49">==</span><span style="color:#032F62"> '</span><span style="color:#005CC5">\\</span><span style="color:#032F62">' </span><span style="color:#D73A49">then</span></span>
<span class="line"><span style="color:#24292E"> psr4_namespace </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> psr4_namespace</span><span style="color:#24292E">:</span><span style="color:#005CC5">sub</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">, </span><span style="color:#D73A49">-</span><span style="color:#005CC5">2</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#6F42C1"> psr4_dir</span><span style="color:#24292E">:</span><span style="color:#005CC5">sub</span><span style="color:#24292E">(</span><span style="color:#D73A49">-</span><span style="color:#005CC5">1</span><span style="color:#24292E">, </span><span style="color:#D73A49">-</span><span style="color:#005CC5">1</span><span style="color:#24292E">) </span><span style="color:#D73A49">==</span><span style="color:#032F62"> '/' </span><span style="color:#D73A49">then</span></span>
<span class="line"><span style="color:#24292E"> psr4_dir </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> psr4_dir</span><span style="color:#24292E">:</span><span style="color:#005CC5">sub</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">, </span><span style="color:#D73A49">-</span><span style="color:#005CC5">2</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"> -- 対象ファイルが置かれたディレクトリとトップレベルのディレクトリを比較し、その差分を名前空間とする</span></span>
<span class="line"><span style="color:#D73A49"> local</span><span style="color:#24292E"> namespace_root_dir </span><span style="color:#D73A49">=</span><span style="color:#24292E"> vim.</span><span style="color:#6F42C1">fs</span><span style="color:#24292E">.</span><span style="color:#005CC5">dirname</span><span style="color:#24292E">(path_to_composer_json) </span><span style="color:#D73A49">..</span><span style="color:#032F62"> '/' </span><span style="color:#D73A49">..</span><span style="color:#24292E"> psr4_dir</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#D73A49"> not</span><span style="color:#24292E"> vim.</span><span style="color:#005CC5">startswith</span><span style="color:#24292E">(current_dir, namespace_root_dir) </span><span style="color:#D73A49">then</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#005CC5"> nil</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"><span style="color:#D73A49"> local</span><span style="color:#24292E"> current_path_suffix </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> current_dir</span><span style="color:#24292E">:</span><span style="color:#005CC5">sub</span><span style="color:#24292E">(</span><span style="color:#D73A49">#</span><span style="color:#24292E">namespace_root_dir </span><span style="color:#D73A49">+</span><span style="color:#005CC5"> 1</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49"> local</span><span style="color:#24292E"> namespace </span><span style="color:#D73A49">=</span><span style="color:#24292E"> psr4_namespace </span><span style="color:#D73A49">..</span><span style="color:#6F42C1"> current_path_suffix</span><span style="color:#24292E">:</span><span style="color:#005CC5">gsub</span><span style="color:#24292E">(</span><span style="color:#032F62">'/'</span><span style="color:#24292E">, </span><span style="color:#032F62">'</span><span style="color:#005CC5">\\</span><span style="color:#032F62">'</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#24292E"> (</span><span style="color:#032F62">"namespace %s;"</span><span style="color:#24292E">):</span><span style="color:#005CC5">format</span><span style="color:#24292E">(namespace)</span></span>
<span class="line"><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">local</span><span style="color:#D73A49"> function</span><span style="color:#6F42C1"> generate_template</span><span style="color:#24292E">()</span></span>
<span class="line"><span style="color:#D73A49"> local</span><span style="color:#24292E"> lines </span><span style="color:#D73A49">=</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#032F62"> '<?php'</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#032F62"> ''</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#032F62"> 'declare(strict_types=1);'</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#032F62"> ''</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#24292E"> }</span></span>
<span class="line"><span style="color:#D73A49"> local</span><span style="color:#24292E"> namespace_decl </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> generate_namespace_declaration</span><span style="color:#24292E">()</span></span>
<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> namespace_decl </span><span style="color:#D73A49">then</span></span>
<span class="line"><span style="color:#24292E"> lines[</span><span style="color:#D73A49">#</span><span style="color:#24292E">lines </span><span style="color:#D73A49">+</span><span style="color:#005CC5"> 1</span><span style="color:#24292E">] </span><span style="color:#D73A49">=</span><span style="color:#24292E"> namespace_decl</span></span>
<span class="line"><span style="color:#24292E"> lines[</span><span style="color:#D73A49">#</span><span style="color:#24292E">lines </span><span style="color:#D73A49">+</span><span style="color:#005CC5"> 1</span><span style="color:#24292E">] </span><span style="color:#D73A49">=</span><span style="color:#032F62"> ''</span></span>
<span class="line"><span style="color:#D73A49"> end</span></span>
<span class="line"><span style="color:#24292E"> lines[</span><span style="color:#D73A49">#</span><span style="color:#24292E">lines </span><span style="color:#D73A49">+</span><span style="color:#005CC5"> 1</span><span style="color:#24292E">] </span><span style="color:#D73A49">=</span><span style="color:#032F62"> ''</span></span>
<span class="line"><span style="color:#D73A49"> return</span><span style="color:#24292E"> lines</span></span>
<span class="line"><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> vim.</span><span style="color:#6F42C1">fn</span><span style="color:#24292E">.</span><span style="color:#005CC5">line</span><span style="color:#24292E">(</span><span style="color:#032F62">'$'</span><span style="color:#24292E">) </span><span style="color:#D73A49">==</span><span style="color:#005CC5"> 1</span><span style="color:#D73A49"> and</span><span style="color:#24292E"> vim.</span><span style="color:#6F42C1">fn</span><span style="color:#24292E">.</span><span style="color:#005CC5">getline</span><span style="color:#24292E">(</span><span style="color:#005CC5">1</span><span style="color:#24292E">) </span><span style="color:#D73A49">==</span><span style="color:#032F62"> '' </span><span style="color:#D73A49">then</span></span>
<span class="line"><span style="color:#6A737D"> -- 対象ファイルが空なら、テンプレートを挿入してカーソルを末尾に移動させる</span></span>
<span class="line"><span style="color:#6A737D"> -- :help setline()</span></span>
<span class="line"><span style="color:#6A737D"> -- :help cursor()</span></span>
<span class="line"><span style="color:#24292E"> vim.</span><span style="color:#6F42C1">fn</span><span style="color:#24292E">.</span><span style="color:#005CC5">setline</span><span style="color:#24292E">(</span><span style="color:#005CC5">1</span><span style="color:#24292E">, </span><span style="color:#005CC5">generate_template</span><span style="color:#24292E">())</span></span>
<span class="line"><span style="color:#24292E"> vim.</span><span style="color:#6F42C1">fn</span><span style="color:#24292E">.</span><span style="color:#005CC5">cursor</span><span style="color:#24292E">(</span><span style="color:#032F62">'$'</span><span style="color:#24292E">, </span><span style="color:#005CC5">0</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">vim.</span><span style="color:#6F42C1">b</span><span style="color:#24292E">.</span><span style="color:#6F42C1">did_ftplugin_php_after</span><span style="color:#D73A49"> =</span><span style="color:#005CC5"> true</span></span></code></pre>
</div>
</section>
<section id="section--outro">
<h2><a href="#section--outro">おわりに</a></h2>
<p>
簡易的な実装だが、多くのケースではうまく動いているようだ。最大の問題は PSR 4 に準拠しないフレームワークを用いているとまったく役に立たないことで、今まさに職場で困っている。こちらはいずれ改良したい。
</p>
</section>
</div>
</article>
</main>
<footer class="footer">
© 2021 nsfisis
</footer>
</body>
</html>
|