epwalsh / obsidian.nvim

Obsidian 🤝 Neovim
Apache License 2.0
3.8k stars 169 forks source link

Note contents preview on link hover #656

Open vcavallo opened 1 month ago

vcavallo commented 1 month ago

🚀 The feature, motivation and pitch

Add a command to get a Telescope preview of the first n lines of the file under the cursor.

Alternatives

No response

Additional context

I'm doing some source-diving now, but I would imagine obsidian.vim's gf / FollowLink internals could be leveraged to take the proper file path and pass it to Telescope to display a preview of the file.

This is a snippet of some GPT hogwash that almost works much of the time, but I don't yet understand the internals of Telescope, obsidian.vim or Lua to open up a proper PR:

  local client = obsidian.get_client()

  client:resolve_link_async(nil, function(result)
    if result then
      if result.path then
        local pickers = require("telescope.pickers")
        local finders = require("telescope.finders")
        local conf = require("telescope.config").values
        local actions = require("telescope.actions")
        local action_state = require("telescope.actions.state")

        pickers.new({}, {
          prompt_title = "Preview: " .. result.name,
          finder = finders.new_oneshot_job({"cat", tostring(result.path)}, {}),
          sorter = conf.generic_sorter({}),
          previewer = conf.file_previewer({}),
          attach_mappings = function(prompt_bufnr, map)
            actions.select_default:replace(function()
              actions.close(prompt_bufnr)
              vim.cmd("edit " .. tostring(result.path))
            end)
            return true
          end,
        }):find()
      end
    end
vcavallo commented 1 month ago

Here's a less breaky version of the GPT generated attempt above:

local obsidian = require("obsidian")

_G.preview_obsidian_link = function()
  local client = obsidian.get_client()
  if not client then
    vim.api.nvim_err_writeln("Obsidian client not initialized")
    return
  end

  client:resolve_link_async(nil, function(...)
    local results = {...}
    vim.schedule(function()
      if #results == 0 then
        vim.api.nvim_err_writeln("No link found under cursor")
        return
      end

      local result = results[1]  -- Take the first result

      if result.path then
        local path_str = tostring(result.path)
        local previewers = require("telescope.previewers")
        local pickers = require("telescope.pickers")
        local finders = require("telescope.finders")
        local conf = require("telescope.config").values

        pickers.new({}, {
          prompt_title = "Preview: " .. vim.fn.fnamemodify(path_str, ":t"),
          finder = finders.new_table {
            results = { path_str },
            entry_maker = function(entry)
              return {
                value = entry,
                display = vim.fn.fnamemodify(entry, ":t"),
                ordinal = entry,
              }
            end
          },
          previewer = previewers.new_buffer_previewer({
            title = "File Preview",
            define_preview = function(self, entry)
              local lines = vim.fn.readfile(entry.value)
              vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, lines)
              vim.api.nvim_buf_set_option(self.state.bufnr, "filetype", "markdown")
              if result.line then
                vim.api.nvim_win_set_cursor(self.state.winid, {result.line, 0})
              end
            end
          }),
          sorter = conf.generic_sorter({}),
          attach_mappings = function(prompt_bufnr)
            require("telescope.actions").select_default:replace(function()
              require("telescope.actions").close(prompt_bufnr)
              vim.cmd("edit " .. path_str)
              if result.line then
                vim.api.nvim_win_set_cursor(0, {result.line, 0})
              end
            end)
            return true
          end,
        }):find()
      elseif result.url then
        vim.api.nvim_out_write("URL link: " .. result.url .. "\n")
        if client.opts.follow_url_func then
          client.opts.follow_url_func(result.url)
        end
      else
        vim.api.nvim_err_writeln("Could not resolve link")
      end
    end)
  end)
end

vim.api.nvim_set_keymap("n", "<leader>op", "<cmd>lua preview_obsidian_link()<CR>", {noremap = true, silent = true})

This actually works fairly well for me as a hack! it loads a Telescope picker with a single entry (the hovered file) and displays the preview window.

mcom1 commented 1 month ago

This is already possible when using marksman lsp for markdown. it supports wikilinks, but it does not work when referencing a file using the id in the frontmatter, only the filename or path. So maybe theres an benefit if its implemented in obsidian.nvim since it relies a lot on frontmatter information by default if im not wrong.