Skip to main content

セッション復元機能付き再起動コマンドの作成

s-show
s-show

20 days前に投稿

0
Neovim: 0.12+
License: MIT

LUA
if vim.fn.exists(':restart') >= 1 then
  local M = {}

  -- 保存先(Neovimの state ディレクトリ配下)
  local base = vim.fn.stdpath("state") .. "/autorestore"
  local session = base .. "/last_session.vim"
  local flag = base .. "/restore.flag"

  local function ensure_dir()
    if vim.fn.isdirectory(base) == 0 then
      vim.fn.mkdir(base, "p")
    end
  end

  local function file_exists(path)
    return vim.uv.fs_stat(path) ~= nil
  end

  local function rm(path)
    -- 存在しない場合のエラーは無視したいので pcall
    pcall(vim.uv.fs_unlink, path)
  end
  -- --------------------------------------

  -- 1) 再起動前に状態を保存してフラグを立てる
  function M.save_for_restart()
    ensure_dir()
    -- セッションに必要なオプション(端末バッファも復元したい場合は 'terminal' を入れる)
    vim.opt.sessionoptions:append("terminal")
    vim.opt.sessionoptions:append("buffers")
    vim.opt.sessionoptions:append("resize")
    vim.opt.sessionoptions:append("tabpages")

    -- セッション + shada 保存
    vim.cmd("silent! mksession! " .. vim.fn.fnameescape(session))
    vim.cmd("silent! wshada!")

    -- フラグ作成(軽いメタ情報)
    local cwd = (vim.uv.cwd and vim.uv.cwd()) or vim.fn.getcwd() or ""
    vim.fn.writefile({
      ("cwd=%s"):format(cwd),
      ("time=%s"):format(os.date("!%Y-%m-%dT%H:%M:%SZ")),
      ("nvim=%s"):format(vim.v.progpath or vim.v.progname or "nvim"),
    }, flag)

    if vim.fn.exists(":restart") == 2 then
      vim.notify("State saved. Restarting Neovim…", vim.log.levels.INFO)
      vim.cmd('silent! wall')
      vim.defer_fn(function()
        vim.cmd("silent! restart")
      end, 1000)
    else
      vim.notify("State saved. Quit and start Neovim again to auto-restore.", vim.log.levels.INFO)
    end
  end

  -- 2) 起動時に自動で復元
  function M.maybe_restore_on_start()
    if not (file_exists(flag) and file_exists(session)) then
      return
    end

    -- LSP 警告回避のため、関数ラップで渡す
    pcall(function() vim.cmd("silent! rshada!") end)
    pcall(function() vim.cmd("silent! source " .. vim.fn.fnameescape(session)) end)

    -- 片付け
    rm(flag)
    rm(session)

    vim.schedule(function()
      vim.notify("Restored previous session.", vim.log.levels.INFO)
    end)
  end

  vim.api.nvim_create_user_command("RestartWithRestore", function()
    M.save_for_restart()
  end, { desc = "Save session+shada and restart (or prompt manual restart)" })

  vim.api.nvim_create_autocmd("VimEnter", {
    callback = function()
      vim.schedule(M.maybe_restore_on_start)
    end,
  })

  return M
end

設定の概要

Neovim v0.12 で :restart コマンドが導入されましたが、このコマンド単体では再起動前の状態を復元する機能はありません。そこで、:restart コマンドの前後にセッション保存・自動復元の処理を追加し、Neovim を再起動しても作業状態をなるべくそのまま戻せるようにしています。
この設定を入れると、:RestartWithRestore というユーザーコマンドが使えるようになります。

実装の解説

実行すると、まず stdpath("state") 配下に専用ディレクトリを作成し、現在のセッションを mksession! で保存します。あわせて wshada! も実行するため、レジスタや履歴など Shada に保存される情報も明示的に書き出します。
セッション保存時には、sessionoptionsterminalbuffersresizetabpages を追加しています。そのため、通常のバッファだけでなく、タブページ構成やウィンドウサイズ、端末バッファも復元対象に含めることを意図しています。
保存が終わると、v0.12 以降で :restart が利用できる環境では :wall で変更済みバッファを保存したうえで、少し待ってから :restart を実行します。再起動後は VimEnter のタイミングでフラグファイルとセッションファイルの有無を確認し、前回保存した Shada とセッションを読み込んで、直前の作業状態を復元します。
また、:restart が存在しない環境では、セッションと Shada の保存だけを行い、「Neovim を終了して起動し直せば自動復元できる」というフォールバック動作になります。コード全体も vim.fn.exists(':restart') >= 1 で囲まれているため、v0.12 以前の環境でもエラーになりません。


コメントを投稿するにはログインが必要です