From 4681e861efcdf9b0b73e3803e70712bc55162863 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 8 Aug 2026 10:38:18 +0900 Subject: feat(slides): improve slide controller --- .../index.html | 260 +++++++++++---------- 1 file changed, 132 insertions(+), 128 deletions(-) (limited to 'services/nuldoc/public/blog/posts/2024-01-10') diff --git a/services/nuldoc/public/blog/posts/2024-01-10/neovim-insert-namespace-declaration-to-empty-php-file/index.html b/services/nuldoc/public/blog/posts/2024-01-10/neovim-insert-namespace-declaration-to-empty-php-file/index.html index bf5ddcea..9e69902a 100644 --- a/services/nuldoc/public/blog/posts/2024-01-10/neovim-insert-namespace-declaration-to-empty-php-file/index.html +++ b/services/nuldoc/public/blog/posts/2024-01-10/neovim-insert-namespace-declaration-to-empty-php-file/index.html @@ -15,7 +15,7 @@ 【Neovim】 空の PHP ファイルに namespace 宣言を挿入する|REPL: Rest-Eat-Program Loop - +
@@ -100,18 +100,20 @@ Neovim で空の PHP ファイルを開いたとき、そのファイルが置かれているディレクトリの構造に基づいて、自動的に 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 には対応していない。 @@ -135,13 +137,14 @@ ファイルタイプは読み込んだあとに変更されることもあるので、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
+
@@ -173,119 +176,120 @@ 実装を簡単にするため、Composer を用いない場合や PSR 4 以外のオートロード規則を使う場合には対応しない。少々長くなるが、以下にスクリプト全文を載せる。

-
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
+
-- cgit v1.3.1