return { { 'folke/which-key.nvim', event = 'VeryLazy', init = function() vim.o.timeout = true vim.o.timeoutlen = 300 end, config = function() vim.o.timeout = true vim.o.timeoutlen = 300 local wk = require("which-key") -- default keymap wk.add({ { "", function() require("dial.map").manipulate("increment", "normal") end , desc = "Increment" }, { "", function() require("dial.map").manipulate("decrement", "normal") end , desc = "Decrement" }, }) -- leader keymap wk.add({ { "c", group = "Convert Commands" }, { mode = { "n", "v" }, { "cgi", function() require("dial.map").manipulate("increment", "gnormal") end , desc = "Increment" }, { "cgd", function() require("dial.map").manipulate("decrement", "gnormal") end , desc = "Decrement" }, }, }) wk.add({ { "l", group = "LSP" }, { mode = { "n", "v" }, { "lR", function() vim.lsp.buf.rename() vim.cmd('silent! wa') end, desc = "Rename (リネーム)" }, { "l,", "lua vim.diagnostic.goto_previous()", desc = "Previous diagnostic (前の診断)" }, { "l.", "lua vim.diagnostic.goto_next()", desc = "Next diagnostic (次の診断)" }, { "la", "lua vim.lsp.buf.code_action()", desc = "Code action (コードアクション)" }, { "ld", "lua require('telescope.builtin').lsp_definitions()", desc = "Go to definition (定義へジャンプ)" }, { "lf", "lua vim.lsp.buf.format()", desc = "Format (フォーマット)" }, { "li", "lua vim.lsp.buf.implementation()", desc = "Go to implementation (実装へジャンプ)" }, { "lk", "lua vim.lsp.buf.hover()", desc = "Hover info (ホバー情報)" }, { "lo", "lua vim.diagnostic.open_float()", desc = "Open diagnostic float (診断をフロート表示)" }, { "lr", "lua require('telescope.builtin').lsp_references()", desc = "Show references (参照を表示)" }, { "ls", "lua vim.lsp.buf.signature_help()", desc = "Signature help (シグネチャヘルプ)" }, { "lD", "lua vim.lsp.buf.type_definition()", desc = "Go to type definition (型定義へジャンプ)" }, }, { { "lt", group = "LSP: Troubles (問題一覧)" }, mode = { "n", "v" }, { "ltd", "Trouble diagnostics toggle", desc = "Diagnostics (診断一覧)" }, { "ltb", "Trouble diagnostics toggle filter.buf=0", desc = "Buffer Diagnostics (バッファ診断)" }, { "lts", "Trouble symbols toggle focus=false", desc = "Symbols (シンボル一覧)" }, { "ltl", "Trouble lsp toggle focus=false win.position=right", desc = "LSP Definitions / references (定義/参照一覧)" }, { "lti", "Trouble loclist toggle", desc = "Location List (ロケーションリスト)" }, { "ltq", "Trouble qflist toggle", desc = "Quickfix List (Quickfixリスト)" }, }, } ) if not vim.g.vscode then wk.add({ { "e", group = "NvimTree" }, { "ee", "NvimTreeFindFile", desc = "NvimTreeFindFile" }, { "ef", "NvimTreeFindFile", desc = "NvimTreeFindFile" }, { "eq", "NvimTreeClose", desc = "NvimTreeClose" }, { "er", "NvimTreeRefresh", desc = "NvimTreeRefresh" }, }) -- パネル・ウィンドウを閉じる操作(IntelliJスタイル) wk.add({ { "q", group = "Close Panel" }, { "qe", "NvimTreeClose", desc = "ツリーを閉じる" }, { "qt", "Trouble close", desc = "Troubleを閉じる" }, { "qT", "ToggleTermToggleAll", desc = "ターミナルを閉じる" }, { "qc", "cclose", desc = "Quickfixを閉じる" }, { "ql", "lclose", desc = "Locationlistを閉じる" }, { "qa", function() -- 全パネルを閉じる vim.cmd("NvimTreeClose") vim.cmd("Trouble close") vim.cmd("cclose") vim.cmd("lclose") pcall(function() vim.cmd("ToggleTermToggleAll") end) end, desc = "全パネルを閉じる" }, }) local function open_in_new_tab(cmd) vim.cmd("tabnew") local win = vim.api.nvim_get_current_win() local buf = vim.api.nvim_get_current_buf() vim.cmd(cmd) return { win = win, buf = buf } end wk.add({ { "d", group = "Debug" }, { "db", function() require('dap').toggle_breakpoint() end, desc = "toggle breakpoint" }, { "dc", function() require('dap').continue() end, desc = "continue" }, { "do", function() require('dap').step_over() end, desc = "step over" }, { "di", function() require('dap').step_into() end, desc = "step into" }, { 'du', require 'dapui'.toggle, desc = 'Debug: Toggle UI' }, { 'dh', function() require 'dap.ui.widgets'.hover() end, desc = 'Debug: Hover' }, { 'dp', function() require 'dap.ui.widgets'.preview() end, desc = 'Debug: Preview' }, { 'dh', function() require 'dap.ui.widgets'.hover() end, desc = 'Debug: Hover' }, -- テスト関連の設定を追加 { "t", group = "Test" }, { "tt", function() require("neotest").run.run() end, desc = "Run Nearest" }, { "tf", function() require("neotest").run.run(vim.fn.expand("%")) end, desc = "Run File" }, { "td", function() local neotest = require("neotest") local ts_utils = require("nvim-treesitter.ts_utils") local node = ts_utils.get_node_at_cursor() local test_name = nil local class_name = nil while node do local node_type = node:type() if node_type == "function_definition" then local name_node = node:field("name")[1] if name_node then local name = vim.treesitter.get_node_text(name_node, 0) if string.match(name, "^test_") then test_name = name break end end elseif node_type == "method_declaration" then -- メソッド名を取得 local name_node = node:field("name")[1] if name_node then local method_text = vim.treesitter.get_node_text(node, 0) -- @Testアノテーションを含むかチェック if method_text:match("@Test") then test_name = vim.treesitter.get_node_text(name_node, 0) break end end end node = node:parent() end if not test_name then vim.notify("No test found near cursor", vim.log.levels.ERROR) return nil end -- pytestの場合、-kオプションでテストを指定 position = { id = test_name } if not position then vim.notify("No test found near cursor", vim.log.levels.ERROR) return nil end local function get_test_debug_config(filetype) local configs = { java = { strategy = "dap", extra_args = { "-Dmaven.surefire.debug", "-DforkCount=0", "-DreuseForks=false" }, scope = "nearest", dap = { justMyCode = false, testScope = "method", console = "integratedTerminal", hotReload = "auto" } }, python = { position = position.id, strategy = "dap", scope = "nearest", extra_args = { "-k", position.id }, dap = { justMyCode = false, console = "integratedTerminal" } } } return configs[filetype] or { strategy = "dap", scope = "nearest" } end local filetype = vim.bo.filetype local config = get_test_debug_config(filetype) require("neotest").run.run( config ) end, desc = "Debug Test" }, { "ts", function() require("neotest").summary.toggle() end, desc = "Toggle Summary" }, { "to", function() require("neotest").output.open({ enter = true }) end, desc = "Show Output" }, }) wk.add( { { "f", group = "telescope" }, { "fr", "Telescope file_browser", desc = "file browser" }, { "fb", "lua require('telescope.builtin').buffers()", desc = "find buffers" }, { "ff", "lua require('telescope.builtin').find_files()", desc = "find files" }, { "fg", "lua require('telescope.builtin').live_grep()", desc = "live grep" }, { "fc", "lua require('telescope.builtin').commands()", desc = "show commands" }, { "fm", "lua require('telescope.builtin').marks()", desc = "show marks" }, { "fv", "lua require('telescope.builtin').registers()", desc = "show registers" }, { "fy", "lua require('neoclip.fzf')({'a', 'star', 'plus', 'unnmaed'})", desc = "yank" }, { mode = { "n", "i", "v", "t" }, { "", "ToggleTermToggleAll", desc = "close toggle" } } } ) wk.add( { { "g", group = "Git" }, { "gg", "LazyGit", desc = "open lazygit" }, { "gs", "lua require('telescope.builtin').git_status()", desc = "git status files" }, { "gb", "BlamerToggle", desc = "show git blame" }, { "gvo", "DiffviewOpen", desc = "show git diff" }, { "gvc", "DiffviewClose", desc = "close git diff" }, -- octo.nvim { "go", group = "Octo" }, -- 基本 (3キー) { "gop", "Octo pr", desc = "現在のPR" }, { "gon", "Octo pr create", desc = "PR作成" }, { "gof", "gf", desc = "ファイルを開く" }, -- レビュー (3キー) { "goS", "Octo review start", desc = "レビュー開始" }, { "gor", "Octo review resume", desc = "レビュー再開" }, { "gos", "Octo review submit", desc = "レビュー提出" }, { "god", "Octo review discard", desc = "レビュー破棄" }, -- コメント (4キー) { "gom", group = "Octo Comment" }, { "goma", "Octo comment add", desc = "追加" }, { "goms", "Octo suggestion", desc = "提案" }, { "gomd", "Octo comment delete", desc = "削除" }, { "gomr", "Octo thread resolve", desc = "解決" }, -- ブラウザ (4キー) { "gob", group = "Octo Browser" }, { "gobb", "Octo pr browser", desc = "ブラウザで開く" }, { "gobr", "Octo pr reload", desc = "リロード" }, -- リアクション (4キー) { "goa", group = "Octo Reaction" }, { "goa+", "Octo reaction thumbs_up", desc = "👍" }, { "goa-", "Octo reaction thumbs_down", desc = "👎" }, { "goah", "Octo reaction heart", desc = "❤️" }, { "goae", "Octo reaction eyes", desc = "👀" }, { "goar", "Octo reaction rocket", desc = "🚀" }, { "goap", "Octo reaction hooray", desc = "🎉" }, } ) -- GitHub リンク・パスコピー関連のヘルパー関数 local function get_repo_root() local root = vim.fn.system('git rev-parse --show-toplevel 2>/dev/null'):gsub('\n', '') if vim.v.shell_error ~= 0 then return nil end return root end local function get_relative_path() local file = vim.fn.expand('%:p') local root = get_repo_root() if not root then vim.notify('Gitリポジトリ内ではありません', vim.log.levels.ERROR) return nil end return file:sub(#root + 2) end local function get_github_base_url() local remote = vim.fn.system('git remote get-url origin 2>/dev/null'):gsub('\n', '') if vim.v.shell_error ~= 0 then return nil end -- SSH形式をHTTPS形式に変換 remote = remote:gsub('git@github%.com:', 'https://github.com/') remote = remote:gsub('%.git$', '') return remote end local function get_line_range() local mode = vim.fn.mode() if mode == 'v' or mode == 'V' or mode == '\22' then local start_line = vim.fn.line('v') local end_line = vim.fn.line('.') if start_line > end_line then start_line, end_line = end_line, start_line end if start_line == end_line then return string.format('#L%d', start_line) else return string.format('#L%d-L%d', start_line, end_line) end else return string.format('#L%d', vim.fn.line('.')) end end local function copy_relative_path() local path = get_relative_path() if path then vim.fn.setreg('+', path) vim.notify('コピー: ' .. path) end end local function copy_github_link() local base_url = get_github_base_url() local path = get_relative_path() if not base_url or not path then vim.notify('GitHub URLを取得できません', vim.log.levels.ERROR) return end local branch = vim.fn.system('git branch --show-current 2>/dev/null'):gsub('\n', '') local line_ref = get_line_range() local url = string.format('%s/blob/%s/%s%s', base_url, branch, path, line_ref) vim.fn.setreg('+', url) vim.notify('コピー: ' .. url) end local function copy_github_permalink() local base_url = get_github_base_url() local path = get_relative_path() if not base_url or not path then vim.notify('GitHub URLを取得できません', vim.log.levels.ERROR) return end local commit = vim.fn.system('git rev-parse HEAD 2>/dev/null'):gsub('\n', '') local line_ref = get_line_range() local url = string.format('%s/blob/%s/%s%s', base_url, commit, path, line_ref) vim.fn.setreg('+', url) vim.notify('コピー: ' .. url) end wk.add( { { "gy", group = "Copy Link/Path" }, { mode = { "n", "v" }, { "gyp", copy_relative_path, desc = "相対パス" }, { "gyl", copy_github_link, desc = "GitHub リンク" }, { "gyL", copy_github_permalink, desc = "Permalink" }, { "gyu", "Octo pr url", desc = "PR URL" }, { "gyc", "Octo comment url", desc = "コメントURL" }, }, } ) wk.add({ { "s", group = "Image Commands" }, { mode = { "v" }, { "sc", function() require("nvim-silicon").clip() end , desc = "Silicon save clipboard" }, { "sf", function() require("nvim-silicon").file() end , desc = "Silicon save file" }, { "sv", "PasteImage" , desc = "Silicon save file" }, }, }) -- flash.nvimのキーマップはflash.lua内で定義されています wk.add({ { "w", group = "Window" }, { mode = { "n", }, { "wt", "WinResizerStartResize", desc = "window resize" }, }, }) -- AI アシスタント (avante.nvim) wk.add({ { "ai", group = "AI Assistant" }, { mode = { "n", "v" }, { "aia", "AvanteAsk", desc = "Avante Ask (Chat)" }, { "air", "AvanteRefresh", desc = "Avante Refresh" }, { "aie", "AvanteEdit", desc = "Avante Edit" }, { "ait", "AvanteToggle", desc = "Avante Toggle" }, { "aic", function() require('avante').apply_cursor() end, desc = "Apply suggestion at cursor" }, { "aiA", function() require('avante').apply_all() end, desc = "Apply all suggestions" }, { "ain", function() require('avante').next_suggestion() end, desc = "Next suggestion" }, { "aip", function() require('avante').previous_suggestion() end, desc = "Previous suggestion" }, }, }) local function register_kotlin_keymaps(bufnr) local ok_api, kotlin_api = pcall(require, 'kotlin-extended-lsp.api') if not ok_api then return end -- LSP基本操作(Kotlin拡張版で上書き) wk.add({ { mode = { "n", "v" }, { "ld", kotlin_api.goto_definition, desc = "Go to definition (TS+LSP)", buffer = bufnr }, { "lD", kotlin_api.goto_type_definition, desc = "Go to type definition", buffer = bufnr }, { "li", kotlin_api.goto_implementation, desc = "Go to implementation", buffer = bufnr }, { "la", kotlin_api.code_actions, desc = "Code action (Kotlin)", buffer = bufnr }, }, }) -- Kotlin拡張機能 wk.add({ { "lx", group = "Kotlin Extended", buffer = bufnr }, { mode = { "n", "v" }, { "lxd", kotlin_api.decompile, desc = "Decompile (逆コンパイル)", buffer = bufnr }, { "lxo", kotlin_api.organize_imports, desc = "Organize imports (import整理)", buffer = bufnr }, { "lxf", kotlin_api.apply_fix, desc = "Apply fix (修正を適用)", buffer = bufnr }, { "lxr", kotlin_api.refactor, desc = "Refactor menu (リファクタリング)", buffer = bufnr }, { "lxl", kotlin_api.lint, desc = "Run Detekt (静的解析)", buffer = bufnr }, }, }) -- テスト関連(Kotlinファイルでのオーバーライド) wk.add({ { mode = { "n", "v" }, { "tt", kotlin_api.test_nearest, desc = "Run nearest test (Kotlin)", buffer = bufnr }, { "tf", kotlin_api.test_file, desc = "Run file tests (Kotlin)", buffer = bufnr }, { "ta", kotlin_api.test_all, desc = "Run all tests (Kotlin)", buffer = bufnr }, }, }) end -- Kotlin用キーバインド(FileType kotlin 時のみ登録) vim.api.nvim_create_autocmd("FileType", { pattern = "kotlin", callback = function() register_kotlin_keymaps(vim.api.nvim_get_current_buf()) end, }) -- which-key読み込み前に開いていたKotlinバッファにも適用 for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do if vim.api.nvim_buf_is_loaded(bufnr) and vim.bo[bufnr].filetype == "kotlin" then register_kotlin_keymaps(bufnr) end end else vim.keymap.set("n", "fb", "call VSCodeNotify('workbench.action.quickOpen')") vim.keymap.set("n", "H", "call VSCodeNotify('workbench.action.previousEditor')") vim.keymap.set("n", "L", "call VSCodeNotify('workbench.action.nextEditor')") end end, dependencies = { 'echasnovski/mini.icons', }, }, }