-- General keymaps local keymap = vim.keymap local opts = { noremap = true, silent = true } -- ============================================================================= -- GENERAL KEYMAPS -- ============================================================================= -- General keymaps keymap.set("i", "jk", "", opts) -- Exit insert mode with jk keymap.set("n", "nh", ":nohl", opts) -- Clear search highlights keymap.set("n", "x", '"_x', opts) -- Delete character without copying into register -- Save and quit (additional) keymap.set("n", "Q", ":qa!", { desc = "Quit all" }) -- Put this in your init.lua local vault_path = vim.env.IDEA_DIR local function follow_obsidian_link() -- Extract the full [[...]] link from the current line under/around the cursor local line = vim.api.nvim_get_current_line() local col = vim.fn.col('.') local start_idx, end_idx, raw -- Search for all [[...]] in the line, pick the one under/around the cursor local i = 1 while true do local s, e = line:find('%[%[.-%]%]', i) if not s then break end if col >= s and col <= e + 1 then start_idx, end_idx = s, e break end i = e + 1 end if not start_idx then vim.notify('No [[link]] under cursor', vim.log.levels.WARN) return end raw = line:sub(start_idx, end_idx) raw = raw:gsub('^!%[%[', '[[') -- strip leading ! from embeds local link = raw:gsub('%[%[', ''):gsub('%]%]', '') -- split off alias (|) and heading (#) only after extracting the full link local alias local heading -- first, split off alias if present local pipe_idx = link:find("|", 1, true) if pipe_idx then alias = link:sub(pipe_idx + 1) link = link:sub(1, pipe_idx - 1) end -- then, split off heading if present local hash_idx = link:find("#", 1, true) if hash_idx then heading = link:sub(hash_idx + 1) link = link:sub(1, hash_idx - 1) end -- normalize spaces link = link:gsub("\\ ", " "):gsub("^%s+", ""):gsub("%s+$", "") local function goto_heading(h) if not h or h == "" then return end -- crude jump: search for markdown heading or block that contains it vim.cmd("keepjumps normal! gg") local pat = "^%s*#+%s*" .. vim.pesc(h) if vim.fn.search(pat) == 0 then -- fallback: plain search vim.fn.search(vim.pesc(h)) end end -- if link contains a '/', treat it as a relative path inside the vault if link:find("/") then local target = vault_path .. "/" .. link .. ".md" vim.cmd.edit(target) goto_heading(heading) return end -- otherwise search recursively for basename match anywhere in the vault local pattern = "**/" .. link .. ".md" local matches = vim.fn.globpath(vault_path, pattern, false, true) -- list if #matches == 1 then vim.cmd.edit(matches[1]) goto_heading(heading) elseif #matches > 1 then vim.ui.select(matches, { prompt = "Multiple matches for " .. link .. ":" }, function(choice) if choice then vim.cmd.edit(choice) goto_heading(heading) end end) else -- not found: offer to create at vault root local target = vault_path .. "/" .. link .. ".md" vim.ui.input({ prompt = "Create " .. target .. " ? (y/N) " }, function(ans) if ans and ans:lower():sub(1, 1) == "y" then vim.cmd.edit(target) -- optional: insert a title if vim.fn.line("$") == 1 and vim.fn.getline(1) == "" then vim.api.nvim_buf_set_lines(0, 0, 0, false, { "# " .. (alias or link), "" }) end goto_heading(heading) end end) end end vim.keymap.set("n", "o", follow_obsidian_link, { noremap = true, silent = true })