From d30dfc89bf1b673b2fdc0638766b930adaec228c Mon Sep 17 00:00:00 2001
From: nsfisis namespace 宣言を挿入したい。具体的には、トップレベルの名前空間が MyNamespace であり、ファイル src/Foo/Bar/Baz.php を開いたときに、そのファイルが空であるなら、次のようなテンプレートが自動的に挿入されてほしい。
<?php
-
-namespace MyNamespace\Foo\Bar;
+ <?php
+
+namespace MyNamespace\Foo\Bar;
+ $ nvim --version
-NVIM v0.9.2
-Build type: Release
-LuaJIT 2.1.1693350652
+ $ nvim --version
+NVIM v0.9.2
+Build type: Release
+LuaJIT 2.1.1693350652
+
今回は Lua で処理を記述したため、Vim では動作しない。以下の説明でも Neovim に絞って述べる。また、パス区切りがスラッシュである前提で記述したため、Windows には対応していない。
@@ -114,13 +117,15 @@ LuaJIT 2.1.1693350652
ファイルタイプは読み込んだあとに変更されることもあるので、ftplugin は複数回実行されうる。二重読み込みを防ぐために、did_ftplugin_<FILE_TYPE>_after というバッファローカル変数を定義しておくのが慣習となっている。
if vim.b.did_ftplugin_php_after then
- return
-end
-
--- ここに実際の処理を書く
-
-vim.b.did_ftplugin_php_after = true
+ if vim.b.did_ftplugin_php_after then
+ return
+end
+
+-- ここに実際の処理を書く
+
+vim.b.did_ftplugin_php_after = true
+ if vim.b.did_ftplugin_php_after then
- return
-end
-
--- base_dir を起点としてディレクトリを上向きに辿っていき、composer.json を探す
--- :help vim.fs.find()
-local function find_composer_json(base_dir)
- return vim.fs.find('composer.json', {
- path = base_dir,
- upward = true,
- -- ホームディレクトリまで到達したら探索を打ち切る
- stop = vim.loop.os_homedir(),
- type = 'file',
- })[1]
-end
-
--- JSON ファイルを読み込み、デコードして返す
--- :help readblob()
--- :help vim.json.decode
--- :help luaref-pcall()
-local function load_json(file_path)
- -- readblob() は Vim script では Blob オブジェクトを返すが、Lua から呼ぶと string に変換される
- local ok_read, content = pcall(vim.fn.readblob, file_path)
- if not ok_read then
- return nil
- end
- local ok_decode, obj = pcall(vim.json.decode, content)
- if not ok_decode then
- return nil
- end
- return obj
-end
-
--- 対象ファイルの置かれたディレクトリを基に namespace 宣言を生成する
--- :help nvim_buf_get_name()
--- :help vim.fs.dirname()
-local function generate_namespace_declaration()
- -- composer.json を探し、トップレベルの名前空間とディレクトリを特定する
- local current_dir = vim.fs.dirname(vim.api.nvim_buf_get_name(0))
- local path_to_composer_json = find_composer_json(current_dir)
- if not path_to_composer_json then
- return nil -- failed to locate composer.json
- end
- local composer_json = load_json(path_to_composer_json)
- if not composer_json then
- return nil -- failed to load composer.json
- end
- -- autoload.psr-4 を探し、型が期待される型と一致するかどうか調べる
- local psr4 = vim.tbl_get(composer_json, 'autoload', 'psr-4')
- if not psr4 then
- return nil -- autoload.psr-4 section is absent
- end
- if vim.tbl_count(psr4) ~= 1 then
- return nil -- psr-4 section is ambiguous
- end
- local psr4_namespace, psr4_dir
- for k, v in pairs(psr4) do
- psr4_namespace = k
- psr4_dir = v
- end
- if type(psr4_dir) == 'table' then
- if #psr4_dir == 1 then
- psr4_dir = psr4_dir[1]
- else
- return nil -- psr-4 section is ambiguous
- end
- end
- if type(psr4_namespace) ~= 'string' or type(psr4_dir) ~= 'string' then
- return nil -- psr-4 section is invalid
- end
- -- 末尾のスラッシュとバックスラッシュを取り除いておく
- if psr4_namespace:sub(-1, -1) == '\\' then
- psr4_namespace = psr4_namespace:sub(0, -2)
- end
- if psr4_dir:sub(-1, -1) == '/' then
- psr4_dir = psr4_dir:sub(0, -2)
- end
-
- -- 対象ファイルが置かれたディレクトリとトップレベルのディレクトリを比較し、その差分を名前空間とする
- local namespace_root_dir = vim.fs.dirname(path_to_composer_json) .. '/' .. psr4_dir
- if not vim.startswith(current_dir, namespace_root_dir) then
- return nil
- end
- local current_path_suffix = current_dir:sub(#namespace_root_dir + 1)
- local namespace = psr4_namespace .. current_path_suffix:gsub('/', '\\')
- return ("namespace %s;"):format(namespace)
-end
-
-local function generate_template()
- local lines = {
- '<?php',
- '',
- 'declare(strict_types=1);',
- '',
- }
- local namespace_decl = generate_namespace_declaration()
- if namespace_decl then
- lines[#lines + 1] = namespace_decl
- lines[#lines + 1] = ''
- end
- lines[#lines + 1] = ''
- return lines
-end
-
-if vim.fn.line('$') == 1 and vim.fn.getline(1) == '' then
- -- 対象ファイルが空なら、テンプレートを挿入してカーソルを末尾に移動させる
- -- :help setline()
- -- :help cursor()
- vim.fn.setline(1, generate_template())
- vim.fn.cursor('$', 0)
-end
-
-vim.b.did_ftplugin_php_after = true
+ if vim.b.did_ftplugin_php_after then
+ return
+end
+
+-- base_dir を起点としてディレクトリを上向きに辿っていき、composer.json を探す
+-- :help vim.fs.find()
+local function find_composer_json(base_dir)
+ return vim.fs.find('composer.json', {
+ path = base_dir,
+ upward = true,
+ -- ホームディレクトリまで到達したら探索を打ち切る
+ stop = vim.loop.os_homedir(),
+ type = 'file',
+ })[1]
+end
+
+-- JSON ファイルを読み込み、デコードして返す
+-- :help readblob()
+-- :help vim.json.decode
+-- :help luaref-pcall()
+local function load_json(file_path)
+ -- readblob() は Vim script では Blob オブジェクトを返すが、Lua から呼ぶと string に変換される
+ local ok_read, content = pcall(vim.fn.readblob, file_path)
+ if not ok_read then
+ return nil
+ end
+ local ok_decode, obj = pcall(vim.json.decode, content)
+ if not ok_decode then
+ return nil
+ end
+ return obj
+end
+
+-- 対象ファイルの置かれたディレクトリを基に namespace 宣言を生成する
+-- :help nvim_buf_get_name()
+-- :help vim.fs.dirname()
+local function generate_namespace_declaration()
+ -- composer.json を探し、トップレベルの名前空間とディレクトリを特定する
+ local current_dir = vim.fs.dirname(vim.api.nvim_buf_get_name(0))
+ local path_to_composer_json = find_composer_json(current_dir)
+ if not path_to_composer_json then
+ return nil -- failed to locate composer.json
+ end
+ local composer_json = load_json(path_to_composer_json)
+ if not composer_json then
+ return nil -- failed to load composer.json
+ end
+ -- autoload.psr-4 を探し、型が期待される型と一致するかどうか調べる
+ local psr4 = vim.tbl_get(composer_json, 'autoload', 'psr-4')
+ if not psr4 then
+ return nil -- autoload.psr-4 section is absent
+ end
+ if vim.tbl_count(psr4) ~= 1 then
+ return nil -- psr-4 section is ambiguous
+ end
+ local psr4_namespace, psr4_dir
+ for k, v in pairs(psr4) do
+ psr4_namespace = k
+ psr4_dir = v
+ end
+ if type(psr4_dir) == 'table' then
+ if #psr4_dir == 1 then
+ psr4_dir = psr4_dir[1]
+ else
+ return nil -- psr-4 section is ambiguous
+ end
+ end
+ if type(psr4_namespace) ~= 'string' or type(psr4_dir) ~= 'string' then
+ return nil -- psr-4 section is invalid
+ end
+ -- 末尾のスラッシュとバックスラッシュを取り除いておく
+ if psr4_namespace:sub(-1, -1) == '\\' then
+ psr4_namespace = psr4_namespace:sub(0, -2)
+ end
+ if psr4_dir:sub(-1, -1) == '/' then
+ psr4_dir = psr4_dir:sub(0, -2)
+ end
+
+ -- 対象ファイルが置かれたディレクトリとトップレベルのディレクトリを比較し、その差分を名前空間とする
+ local namespace_root_dir = vim.fs.dirname(path_to_composer_json) .. '/' .. psr4_dir
+ if not vim.startswith(current_dir, namespace_root_dir) then
+ return nil
+ end
+ local current_path_suffix = current_dir:sub(#namespace_root_dir + 1)
+ local namespace = psr4_namespace .. current_path_suffix:gsub('/', '\\')
+ return ("namespace %s;"):format(namespace)
+end
+
+local function generate_template()
+ local lines = {
+ '<?php',
+ '',
+ 'declare(strict_types=1);',
+ '',
+ }
+ local namespace_decl = generate_namespace_declaration()
+ if namespace_decl then
+ lines[#lines + 1] = namespace_decl
+ lines[#lines + 1] = ''
+ end
+ lines[#lines + 1] = ''
+ return lines
+end
+
+if vim.fn.line('$') == 1 and vim.fn.getline(1) == '' then
+ -- 対象ファイルが空なら、テンプレートを挿入してカーソルを末尾に移動させる
+ -- :help setline()
+ -- :help cursor()
+ vim.fn.setline(1, generate_template())
+ vim.fn.cursor('$', 0)
+end
+
+vim.b.did_ftplugin_php_after = true
+