From 93cac62b08b4e2e255ec8f403c887715ae125b21 Mon Sep 17 00:00:00 2001 From: Onns Date: Tue, 9 Apr 2024 16:08:31 +0800 Subject: [PATCH] Update keymaps.lua --- lua/config/go_keymaps.lua | 53 ++++ lua/config/keymaps.lua | 608 +++++++++++++++++++++++++------------- 2 files changed, 448 insertions(+), 213 deletions(-) create mode 100644 lua/config/go_keymaps.lua diff --git a/lua/config/go_keymaps.lua b/lua/config/go_keymaps.lua new file mode 100644 index 0000000..c7999b9 --- /dev/null +++ b/lua/config/go_keymaps.lua @@ -0,0 +1,53 @@ +function LiveGrepWithExtension(extension) + require('telescope.builtin').live_grep({ + additional_args = function(opts) + return { "--glob", "*." .. extension } + end + }) +end + +vim.api.nvim_create_user_command( + "LgExtension", + function(opts) + LiveGrepWithExtension(opts.args) + end, + { nargs = 1 } +) + + +function LiveGrepUp(n) + local bufnr = vim.api.nvim_get_current_buf() + local full_path = vim.api.nvim_buf_get_name(bufnr) + local file_path = vim.fs.dirname(full_path) + -- 用系统的路径分隔符分割路径 + local path_sep = package.config:sub(1, 1) + local components = {} + + -- 分割路径到各个组件 + for component in string.gmatch(file_path, "[^" .. path_sep .. "]+") do + table.insert(components, component) + end + + -- 计算向上n级的路径 + local up_n = #components - n + if up_n < 1 then + up_n = 1 + end -- 确保不会超出根目录 + local new_cwd = path_sep + for i = 1, up_n do + new_cwd = new_cwd .. components[i] .. (i < up_n and path_sep or "") + end + + print("search under:" .. new_cwd) + -- 使用新的目录执行Telescope live_grep + require("telescope.builtin").live_grep({ cwd = new_cwd }) +end + +vim.api.nvim_create_user_command( + "LgUp", + function(opts) + local n = tonumber(opts.args) or 0 + LiveGrepUp(n) + end, + { nargs = 1 } -- 命令接受一个参数 +) diff --git a/lua/config/keymaps.lua b/lua/config/keymaps.lua index 4ded660..92a5bc3 100644 --- a/lua/config/keymaps.lua +++ b/lua/config/keymaps.lua @@ -2,288 +2,470 @@ -- Default keymaps that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/keymaps.lua -- Add any additional keymaps here +require('config.go_keymaps') + function SaveHttpResp() - -- 获取当前时间并格式化为时间戳 - local timestamp = os.date("%Y%m%d%H%M%S") + -- 获取当前时间并格式化为时间戳 + local timestamp = os.date("%Y%m%d%H%M%S") - -- 获取当前文件的目录路径 - local current_path = vim.fn.expand('%:p:h') + -- 获取当前文件的目录路径 + local current_path = vim.fn.expand("%:p:h") - -- 定位到 rest.nvim 的响应缓冲区 - for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do - if vim.bo[bufnr].filetype == 'httpResult' then - -- 设置保存文件的完整路径,包含时间戳 - local filename = current_path .. "/log/response_" .. timestamp .. ".txt" - vim.api.nvim_buf_call(bufnr, function() - vim.cmd('w ' .. filename) - end) - print("Response saved to " .. filename) - break - end + -- 定位到 rest.nvim 的响应缓冲区 + for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do + if vim.bo[bufnr].filetype == "httpResult" then + -- 设置保存文件的完整路径,包含时间戳 + local filename = current_path .. "/log/response_" .. timestamp .. ".txt" + vim.api.nvim_buf_call(bufnr, function() + vim.cmd("w " .. filename) + end) + print("Response saved to " .. filename) + break end + end end -vim.api.nvim_set_keymap('n', 'rr', - "Rest run", - { noremap = true, silent = true }) - -vim.api.nvim_set_keymap('n', 'rs', - ":lua SaveHttpResp()", - { noremap = true, silent = true }) +vim.api.nvim_set_keymap("n", "rr", "Rest run", { noremap = true, silent = true }) +vim.api.nvim_set_keymap("n", "rs", ":lua SaveHttpResp()", { noremap = true, silent = true }) function GetGoplsRootDir() - local clients = vim.lsp.get_active_clients() - for _, client in ipairs(clients) do - if client.name == "gopls" and client.config.root_dir then - return client.config.root_dir - end + local clients = vim.lsp.get_active_clients() + for _, client in ipairs(clients) do + if client.name == "gopls" and client.config.root_dir then + return client.config.root_dir end - return nil -- 返回 nil 如果没有找到 gopls 或者 gopls 没有根目录 + end + return nil -- 返回 nil 如果没有找到 gopls 或者 gopls 没有根目录 end function GoToPathAndLine(input) - if input == '' then - return - end - local parts = vim.split(input, ':') - local file = parts[1] - local line = 1 - if #parts > 1 then - line = parts[2] - end - -- 如果有工作路径,选第一个,否则选当前路径 - local pwd = vim.fn.getcwd() - -- if vim.g.WorkspaceFolders and #vim.g.WorkspaceFolders > 0 then - -- pwd = vim.g.WorkspaceFolders[1] - -- end - local goplsRootDir = GetGoplsRootDir() - if goplsRootDir then - pwd = goplsRootDir - end - vim.cmd('edit +' .. line .. ' ' .. pwd .. '/' .. file) + if input == "" then + return + end + local parts = vim.split(input, ":") + local file = parts[1] + local line = 1 + if #parts > 1 then + line = parts[2] + end + -- 如果有工作路径,选第一个,否则选当前路径 + local pwd = vim.fn.getcwd() + -- if vim.g.WorkspaceFolders and #vim.g.WorkspaceFolders > 0 then + -- pwd = vim.g.WorkspaceFolders[1] + -- end + local goplsRootDir = GetGoplsRootDir() + if goplsRootDir then + pwd = goplsRootDir + end + vim.cmd("edit +" .. line .. " " .. pwd .. "/" .. file) end -vim.api.nvim_set_keymap('n', 'gto', ':lua GoToPathAndLine(vim.fn.input("Enter path and line: "))', { noremap = true }) +vim.api.nvim_set_keymap( + "n", + "gto", + ':lua GoToPathAndLine(vim.fn.input("Enter path and line: "))', + { noremap = true } +) function ExportExpandToClipboard() - local pwd = vim.fn.getcwd() - local goplsRootDir = GetGoplsRootDir() - if goplsRootDir then - pwd = goplsRootDir - end - local rf = vim.fn.expand('%:p') .. ':' .. vim.fn.line('.') - local expanded = string.sub(rf, string.len(pwd .. '/') + 1) - vim.fn.setreg('+', expanded) - print('Expanded path copied to clipboard: ' .. expanded) + local pwd = vim.fn.getcwd() + local goplsRootDir = GetGoplsRootDir() + if goplsRootDir then + pwd = goplsRootDir + end + local rf = vim.fn.expand("%:p") .. ":" .. vim.fn.line(".") + local expanded = string.sub(rf, string.len(pwd .. "/") + 1) + vim.fn.setreg("+", expanded) + print("Expanded path copied to clipboard: " .. expanded) end -vim.api.nvim_set_keymap('n', 'gcr', ':lua ExportExpandToClipboard()', { noremap = true }) - +vim.api.nvim_set_keymap("n", "gcr", ":lua ExportExpandToClipboard()", { noremap = true }) local function check_spelling() - -- 保存当前文件 - vim.cmd('write') + -- 保存当前文件 + vim.cmd("write") - -- 获取当前文件的路径 - local current_file = vim.fn.expand('%:p') - print('Spell check in: ' .. current_file) + -- 获取当前文件的路径 + local current_file = vim.fn.expand("%:p") + print("Spell check in: " .. current_file) - -- 构建CSpell命令 - local command = 'cspell --config /Users/onns/.onns/weiyun/code/config/vim/cspell.yaml -r "/Users/onns" ' .. - current_file + -- 构建CSpell命令 + local command = 'cspell --config /Users/onns/.onns/weiyun/code/config/vim/cspell.yaml -r "/Users/onns" ' + .. current_file - -- 在新的终端窗口中执行CSpell - vim.cmd('split | terminal ' .. command) + -- 在新的终端窗口中执行CSpell + vim.cmd("split | terminal " .. command) end -- 将Lua函数绑定到Neovim命令 -vim.api.nvim_create_user_command('SpellCheck', check_spelling, {}) +vim.api.nvim_create_user_command("SpellCheck", check_spelling, {}) -vim.api.nvim_set_keymap('n', 'pj', - [[:.s/\v(\w+) \= (\d+).*;/\1 = \2 [(gogoproto.jsontag) = '\1', json_name = '\1'];]], - { noremap = true, silent = true }) -vim.api.nvim_set_keymap('n', 'pf', - [[:.s/\v(\w+) \= (\d+).*;/\1 = \2 [(gogoproto.moretags) = 'form:"\1"',(gogoproto.jsontag) = '\1', json_name = '\1'];]], - { noremap = true, silent = true }) +vim.api.nvim_set_keymap( + "n", + "pj", + [[:.s/\v(\w+) \= (\d+).*;/\1 = \2 [(gogoproto.jsontag) = '\1', json_name = '\1'];]], + { noremap = true, silent = true } +) +vim.api.nvim_set_keymap( + "n", + "pf", + [[:.s/\v(\w+) \= (\d+).*;/\1 = \2 [(gogoproto.moretags) = 'form:"\1"',(gogoproto.jsontag) = '\1', json_name = '\1'];]], + { noremap = true, silent = true } +) function TodoTelescopeWithCWD() - local pwd = vim.fn.getcwd() - local goplsRootDir = GetGoplsRootDir() - if goplsRootDir then - pwd = goplsRootDir - end - print('TodoTelescope at : ' .. pwd) - vim.cmd("TodoTelescope cwd=" .. pwd) + local pwd = vim.fn.getcwd() + local goplsRootDir = GetGoplsRootDir() + if goplsRootDir then + pwd = goplsRootDir + end + print("TodoTelescope at : " .. pwd) + vim.cmd("TodoTelescope cwd=" .. pwd) end -vim.api.nvim_set_keymap('n', 'fw', ':lua TodoTelescopeWithCWD()', { noremap = true }) +vim.api.nvim_set_keymap("n", "fw", ":lua TodoTelescopeWithCWD()", { noremap = true }) -- 待定列表 local targets = { "cmd", "CHANGELOG.md", "go.mod", ".git" } -- 检查文件或目录是否存在 local function file_exists(path) - local ok, err, code = os.rename(path, path) - if not ok then - if code == 13 then - -- Permission denied, but it exists - return true - end + local ok, err, code = os.rename(path, path) + if not ok then + if code == 13 then + -- Permission denied, but it exists + return true end - return ok, err + end + return ok, err end -- 从当前路径向上查找目标文件或目录 local function find_upwards() - -- 获取当前文件的路径 - local path = vim.fn.expand('%:p:h') + -- 获取当前文件的路径 + local path = vim.fn.expand("%:p:h") - while path and path ~= '' do - -- 检查目标文件或目录是否在当前路径 - for _, target in pairs(targets) do - local fullpath = path .. '/' .. target - if file_exists(fullpath) then - return path - end - end - - -- 移动到上一级目录 - path = vim.fn.fnamemodify(path, ':h') + while path and path ~= "" do + -- 检查目标文件或目录是否在当前路径 + for _, target in pairs(targets) do + local fullpath = path .. "/" .. target + if file_exists(fullpath) then + return path + end end - return nil + + -- 移动到上一级目录 + path = vim.fn.fnamemodify(path, ":h") + end + return nil end function TodoTelescopeWithProject() - local pwd = vim.fn.getcwd() - local projectDir = find_upwards() - if projectDir then - pwd = projectDir - end - print('TodoTelescope at : ' .. pwd) - vim.cmd("TodoTelescope cwd=" .. pwd) + local pwd = vim.fn.getcwd() + local projectDir = find_upwards() + if projectDir then + pwd = projectDir + end + print("TodoTelescope at : " .. pwd) + vim.cmd("TodoTelescope cwd=" .. pwd) end -vim.api.nvim_set_keymap('n', 'fp', ':lua TodoTelescopeWithProject()', { noremap = true }) - +vim.api.nvim_set_keymap("n", "fp", ":lua TodoTelescopeWithProject()", { noremap = true }) function InsertGitBranch() - local cwd = vim.fn.getcwd() - local git_branch_cmd = "git -C " .. cwd .. " branch --show-current" - local handle = io.popen(git_branch_cmd) - local git_branch = handle:read("*a") - handle:close() - git_branch = git_branch:gsub("%s+$", "") - if git_branch ~= "" then - -- vim.api.nvim_put({ git_branch }, "", false, true) - local line_num = vim.api.nvim_win_get_cursor(0)[1] - local todo_info = "// TODO: onns " .. git_branch .. " " - vim.api.nvim_buf_set_lines(0, line_num - 1, line_num - 1, false, { todo_info }) - vim.api.nvim_command('startinsert') - vim.api.nvim_win_set_cursor(0, { line_num, #todo_info }) - end + local cwd = vim.fn.getcwd() + local git_branch_cmd = "git -C " .. cwd .. " branch --show-current" + local handle = io.popen(git_branch_cmd) + local git_branch = handle:read("*a") + handle:close() + git_branch = git_branch:gsub("%s+$", "") + if git_branch ~= "" then + -- vim.api.nvim_put({ git_branch }, "", false, true) + local line_num = vim.api.nvim_win_get_cursor(0)[1] + local todo_info = "// TODO: onns " .. git_branch .. " " + vim.api.nvim_buf_set_lines(0, line_num - 1, line_num - 1, false, { todo_info }) + vim.api.nvim_command("startinsert") + vim.api.nvim_win_set_cursor(0, { line_num, #todo_info }) + end end -vim.api.nvim_set_keymap('n', 'ig', ':lua InsertGitBranch()', { noremap = true }) +vim.api.nvim_set_keymap("n", "ig", ":lua InsertGitBranch()", { noremap = true }) - -local ts_utils = require 'nvim-treesitter.ts_utils' +local ts_utils = require("nvim-treesitter.ts_utils") function JumpToFunctionName() - -- vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('[f', true, true, true), 'm', true) - local node = ts_utils.get_node_at_cursor() + -- vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('[f', true, true, true), 'm', true) + local node = ts_utils.get_node_at_cursor() - while node do - -- print(node:type()) - if node:type() == "function_declaration" or node:type() == "method_declaration" then - local child_count = node:child_count() - for i = 0, child_count - 1 do - local child = node:child(i) - -- print("Child " .. i .. ": " .. child:type()) - if child then - if node:type() == "function_declaration" and child:type() == "identifier" then - local start_row, start_col, _, _ = child:range() - vim.api.nvim_win_set_cursor(0, { start_row + 1, start_col }) - return - end - if node:type() == "method_declaration" and child:type() == "field_identifier" then - local start_row, start_col, _, _ = child:range() - vim.api.nvim_win_set_cursor(0, { start_row + 1, start_col }) - return - end - end - end + while node do + -- print(node:type()) + if node:type() == "function_declaration" or node:type() == "method_declaration" then + local child_count = node:child_count() + for i = 0, child_count - 1 do + local child = node:child(i) + -- print("Child " .. i .. ": " .. child:type()) + if child then + if node:type() == "function_declaration" and child:type() == "identifier" then + local start_row, start_col, _, _ = child:range() + vim.api.nvim_win_set_cursor(0, { start_row + 1, start_col }) + return + end + if node:type() == "method_declaration" and child:type() == "field_identifier" then + local start_row, start_col, _, _ = child:range() + vim.api.nvim_win_set_cursor(0, { start_row + 1, start_col }) + return + end end - node = node:parent() + end end + node = node:parent() + end end -vim.api.nvim_set_keymap('n', '[n', 'lua JumpToFunctionName()', { noremap = true, silent = true }) +vim.api.nvim_set_keymap("n", "[n", "lua JumpToFunctionName()", { noremap = true, silent = true }) function GetGoImportPath() - local current_file = vim.api.nvim_buf_get_name(0) - local current_path = vim.fn.fnamemodify(current_file, ":h") - local go_mod_path = vim.fn.findfile("go.mod", current_path .. ";") - if go_mod_path == "" then - return + local current_file = vim.api.nvim_buf_get_name(0) + local current_path = vim.fn.fnamemodify(current_file, ":h") + local go_mod_path = vim.fn.findfile("go.mod", current_path .. ";") + if go_mod_path == "" then + return + end + + local go_mod_content = vim.fn.readfile(go_mod_path) + local module_name = nil + for _, line in ipairs(go_mod_content) do + module_name = line:match("^module%s+(%S+)") + if module_name then + break end + end + if not module_name then + return + end - local go_mod_content = vim.fn.readfile(go_mod_path) - local module_name = nil - for _, line in ipairs(go_mod_content) do - module_name = line:match("^module%s+(%S+)") - if module_name then - break - end - end - if not module_name then - return - end + local module_root = vim.fn.fnamemodify(go_mod_path, ":h") - local module_root = vim.fn.fnamemodify(go_mod_path, ":h") + local package_path = string.sub(current_path, string.len(module_root .. "/") + 1) - local package_path = string.sub(current_path, string.len(module_root .. '/') + 1) - - local import_package_name = module_name .. "/" .. package_path - vim.fn.setreg('+', import_package_name) - print("Import path copied to clipboard: " .. import_package_name) + local import_package_name = module_name .. "/" .. package_path + vim.fn.setreg("+", import_package_name) + print("Import path copied to clipboard: " .. import_package_name) end -vim.api.nvim_set_keymap('n', 'gcp', ':lua GetGoImportPath()', { noremap = true }) +vim.api.nvim_set_keymap("n", "gcp", ":lua GetGoImportPath()", { noremap = true }) -vim.api.nvim_set_keymap('n', 'fm', - ":lua require('bookmarks').toggle_bookmarks()", - { noremap = true, silent = true }) - -function LiveGrepUp(n) - -- 获取当前工作目录 - local cwd = vim.fn.getcwd() - -- 用系统的路径分隔符分割路径 - local path_sep = package.config:sub(1, 1) - local components = {} - - -- 分割路径到各个组件 - for component in string.gmatch(cwd, "[^" .. path_sep .. "]+") do - table.insert(components, component) - end - - -- 计算向上n级的路径 - local up_n = #components - n - if up_n < 1 then up_n = 1 end -- 确保不会超出根目录 - local new_cwd = path_sep - for i = 1, up_n do - new_cwd = new_cwd .. components[i] .. (i < up_n and path_sep or "") - end - - -- 使用新的目录执行Telescope live_grep - require('telescope.builtin').live_grep({ cwd = new_cwd }) -end - -vim.api.nvim_create_user_command( - 'Lg', - function(opts) - local n = tonumber(opts.args) or 0 - LiveGrepUp(n) - end, - { nargs = 1 } -- 命令接受一个参数 +vim.api.nvim_set_keymap( + "n", + "fm", + ":lua require('bookmarks').toggle_bookmarks()", + { noremap = true, silent = true } ) + +local function get_child_index(node) + local parent = node:parent() + if not parent then + return nil, 0 + end -- 如果没有父节点,则返回nil + + local child_count = parent:child_count() + local index = 0 + -- 遍历父节点的所有子节点 + for child in parent:iter_children() do + if child:id() == node:id() then + return index, child_count -- 当找到匹配的子节点时,返回当前索引 + end + index = index + 1 + end + + return nil, child_count -- 如果没有找到匹配的子节点,返回nil +end + +-- TODO: onns main 待实现 +function JumpToCall(direction) + local node = ts_utils.get_node_at_cursor() + local function search_upwards(current_node) + if not current_node then + return nil + end + print("Child " .. i .. ": " .. child:type()) + if current_node:type() == "call_expression" then + return current_node + else + local node_index, _ = get_child_index(node) + if node_index == 0 then + return search_upwards(current_node:parent()) + else + return search_upwards(current_node:parent():child(node_index - 1)) + end + end + end + + local function search_downwards(current_node, count) + if count == 200 then + return nil + end + if not current_node then + return nil + end + if current_node:type() == "call_expression" then + return current_node + end + for child in current_node:iter_children() do + local found = search_downwards(child, count + 1) + if found then + return found + end + end + + while current_node do + local parent = current_node:parent() + local node_index, node_count = get_child_index(current_node) + if node_count == 0 then + return nil + end + if node_index < node_count - 1 then + local child = parent:child(node_index + 1) + local found = search_downwards(child, count + 1) + if found then + return found + end + end + current_node = parent:parent() + end + return nil + end + + local res_node = nil + if direction == "up" then + res_node = search_upwards(node:parent()) + elseif direction == "down" then + res_node = search_downwards(node, 0) + else + error("Direction must be 'up' or 'down'.") + end + if res_node then + print("Child count: ->" .. res_node:type()) + local start_row, start_col, _, _ = res_node:range() + vim.api.nvim_win_set_cursor(0, { start_row + 1, start_col }) + end +end + +vim.api.nvim_set_keymap("n", "]od", 'lua JumpToCall("down")', { noremap = true, silent = true }) + +local function _set_cursor(node) + -- TODO: check the validity of range + local start_row, start_col, _, _ = node:range() + vim.api.nvim_win_set_cursor(0, { start_row + 1, start_col }) +end + +function JumpToNextFuncCall() + local node = ts_utils.get_node_at_cursor() + local ctr = 0 -- ugly prevention of endless loop + + while node do + ctr = ctr + 1 + + local sibling = node:next_sibling() + print(sibling) + -- if the current node does not have a leftmore sibling then we traverse its parent node + if not sibling then + node = node:parent() + goto continue + end + + node = sibling + + -- TODO: which kinds of nodes contain function_call? (for performance enhancement) + -- currently, for all previous nodes we do a DFS traverse to find the first "function_call" + local first_function_call_node = nil + local stack = { node } + + while #stack > 0 do + local current_node = table.remove(stack) + + if current_node:type() == "call_expression" then + first_function_call_node = current_node + break + end + + local children = {} + for child in current_node:iter_children() do + table.insert(children, 1, child) + end + + for _, child in ipairs(children) do + table.insert(stack, child) + end + end + + if first_function_call_node then + _set_cursor(first_function_call_node) + return + end + + if ctr > 1000 then + print("endless loop...") + break + end + + ::continue:: + end +end + +function JumpToLastFuncCall() + local node = ts_utils.get_node_at_cursor() + local ctr = 0 -- ugly prevention of endless loop + + while node do + ctr = ctr + 1 + + local sibling = node:prev_sibling() + print(sibling) + -- if the current node does not have a leftmore sibling then we traverse its parent node + if not sibling then + node = node:parent() + goto continue + end + + node = sibling + + -- TODO: which kinds of nodes contain function_call? (for performance enhancement) + -- currently, for all previous nodes we do a DFS traverse to find the last "function_call" + local last_function_call_node = nil + local stack = { node } + + while #stack > 0 do + local current_node = table.remove(stack) + + if current_node:type() == "call_expression" then + last_function_call_node = current_node + end + + local children = {} + for child in current_node:iter_children() do + table.insert(children, 1, child) + end + + for _, child in ipairs(children) do + table.insert(stack, child) + end + end + + if last_function_call_node then + _set_cursor(last_function_call_node) + return + end + + if ctr > 1000 then + print("endless loop...") + break + end + + ::continue:: + end +end + +vim.api.nvim_set_keymap("n", "]oc", "lua JumpToNextFuncCall()", { noremap = true, silent = true }) +vim.api.nvim_set_keymap("n", "[oc", "lua JumpToLastFuncCall()", { noremap = true, silent = true })