瀏覽代碼

Add the better peek functions (#1172)

Geet Sethi 4 年之前
父節點
當前提交
cf16a2e826
共有 3 個文件被更改,包括 147 次插入66 次删除
  1. 6 0
      lua/core/which-key.lua
  2. 1 66
      lua/lsp/keybinds.lua
  3. 140 0
      lua/lsp/peek.lua

+ 6 - 0
lua/core/which-key.lua

@@ -152,6 +152,12 @@ M.config = function()
           "<cmd>lua vim.lsp.diagnostic.goto_prev({popup_opts = {border = lvim.lsp.popup_border}})<cr>",
           "Prev Diagnostic",
         },
+        p = {
+          name = "Peek",
+          d = { "<cmd>lua require('lsp.peek').Peek('definition')<cr>", "Definition" },
+          t = { "<cmd>lua require('lsp.peek').Peek('typeDefinition')<cr>", "Type Definition" },
+          i = { "<cmd>lua require('lsp.peek').Peek('implementation')<cr>", "Implementation" },
+        },
         q = { "<cmd>Telescope quickfix<cr>", "Quickfix" },
         r = { "<cmd>lua vim.lsp.buf.rename()<cr>", "Rename" },
         s = { "<cmd>Telescope lsp_document_symbols<cr>", "Document Symbols" },

+ 1 - 66
lua/lsp/keybinds.lua

@@ -1,70 +1,5 @@
 local M = {}
 
--- Taken from https://www.reddit.com/r/neovim/comments/gyb077/nvimlsp_peek_defination_javascript_ttserver/
-function M.preview_location(location, context, before_context)
-  -- location may be LocationLink or Location (more useful for the former)
-  context = context or 15
-  before_context = before_context or 0
-  local uri = location.targetUri or location.uri
-  if uri == nil then
-    return
-  end
-  local bufnr = vim.uri_to_bufnr(uri)
-  if not vim.api.nvim_buf_is_loaded(bufnr) then
-    vim.fn.bufload(bufnr)
-  end
-
-  local range = location.targetRange or location.range
-  local contents = vim.api.nvim_buf_get_lines(
-    bufnr,
-    range.start.line - before_context,
-    range["end"].line + 1 + context,
-    false
-  )
-  local filetype = vim.api.nvim_buf_get_option(bufnr, "filetype")
-  return vim.lsp.util.open_floating_preview(contents, filetype, { border = lvim.lsp.popup_border })
-end
-
-function M.preview_location_callback(_, method, result)
-  local context = 15
-  if result == nil or vim.tbl_isempty(result) then
-    print("No location found: " .. method)
-    return nil
-  end
-  if vim.tbl_islist(result) then
-    M.floating_buf, M.floating_win = M.preview_location(result[1], context)
-  else
-    M.floating_buf, M.floating_win = M.preview_location(result, context)
-  end
-end
-
-function M.PeekDefinition()
-  if vim.tbl_contains(vim.api.nvim_list_wins(), M.floating_win) then
-    vim.api.nvim_set_current_win(M.floating_win)
-  else
-    local params = vim.lsp.util.make_position_params()
-    return vim.lsp.buf_request(0, "textDocument/definition", params, M.preview_location_callback)
-  end
-end
-
-function M.PeekTypeDefinition()
-  if vim.tbl_contains(vim.api.nvim_list_wins(), M.floating_win) then
-    vim.api.nvim_set_current_win(M.floating_win)
-  else
-    local params = vim.lsp.util.make_position_params()
-    return vim.lsp.buf_request(0, "textDocument/typeDefinition", params, M.preview_location_callback)
-  end
-end
-
-function M.PeekImplementation()
-  if vim.tbl_contains(vim.api.nvim_list_wins(), M.floating_win) then
-    vim.api.nvim_set_current_win(M.floating_win)
-  else
-    local params = vim.lsp.util.make_position_params()
-    return vim.lsp.buf_request(0, "textDocument/implementation", params, M.preview_location_callback)
-  end
-end
-
 function M.setup()
   if lvim.lsp.default_keybinds then
     vim.cmd "nnoremap <silent> gd <cmd>lua vim.lsp.buf.definition()<CR>"
@@ -78,7 +13,7 @@ function M.setup()
       { noremap = true, silent = true }
     )
 
-    vim.cmd "nnoremap <silent> gp <cmd>lua require'lsp.keybinds'.PeekDefinition()<CR>"
+    vim.cmd "nnoremap <silent> gp <cmd>lua require'lsp.peek'.Peek('definition')<CR>"
     vim.cmd "nnoremap <silent> K :lua vim.lsp.buf.hover()<CR>"
     vim.cmd "nnoremap <silent> <C-p> :lua vim.lsp.diagnostic.goto_prev({popup_opts = {border = lvim.lsp.popup_border}})<CR>"
     vim.cmd "nnoremap <silent> <C-n> :lua vim.lsp.diagnostic.goto_next({popup_opts = {border = lvim.lsp.popup_border}})<CR>"

+ 140 - 0
lua/lsp/peek.lua

@@ -0,0 +1,140 @@
+local M = {
+  floating_buf = nil,
+  floating_win = nil,
+  prev_result = nil,
+}
+
+local function create_floating_file(location, opts)
+  vim.validate {
+    location = { location, "t" },
+    opts = { opts, "t", true },
+  }
+
+  -- Set some defaults
+  opts = opts or {}
+  local close_events = opts.close_events or { "CursorMoved", "CursorMovedI", "BufHidden", "InsertCharPre" }
+
+  -- location may be LocationLink or Location
+  local uri = location.targetUri or location.uri
+  if uri == nil then
+    return
+  end
+  local bufnr = vim.uri_to_bufnr(uri)
+  if not vim.api.nvim_buf_is_loaded(bufnr) then
+    vim.fn.bufload(bufnr)
+  end
+
+  local range = location.targetRange or location.range
+
+  local contents = vim.api.nvim_buf_get_lines(
+    bufnr,
+    range.start.line,
+    math.min(range["end"].line + 1 + (opts.context or 10), range.start.line + (opts.max_height or 15)), -- Don't let the window be more that 15 lines long(height)
+    false
+  )
+  local width, height = vim.lsp.util._make_floating_popup_size(contents, opts)
+  opts = vim.lsp.util.make_floating_popup_options(width, height, opts)
+  -- Don't make it minimal as it is meant to be fully featured
+  opts["style"] = nil
+
+  vim.api.nvim_buf_set_option(bufnr, "bufhidden", "wipe")
+
+  local winnr = vim.api.nvim_open_win(bufnr, false, opts)
+  vim.api.nvim_win_set_option(winnr, "winblend", 0)
+
+  vim.api.nvim_buf_set_var(bufnr, "lsp_floating_window", winnr)
+
+  -- Set some autocmds to close the window
+  vim.api.nvim_command(
+    "autocmd QuitPre <buffer> ++nested ++once lua pcall(vim.api.nvim_win_close, " .. winnr .. ", true)"
+  )
+  vim.lsp.util.close_preview_autocmd(close_events, winnr)
+
+  return bufnr, winnr
+end
+
+local function preview_location_callback(_, method, result)
+  if result == nil or vim.tbl_isempty(result) then
+    print("peek: No location found: " .. method)
+    return nil
+  end
+
+  local opts = {
+    border = "rounded",
+    context = 10,
+  }
+
+  if vim.tbl_islist(result) then
+    M.prev_result = result[1]
+    M.floating_buf, M.floating_win = create_floating_file(result[1], opts)
+  else
+    M.prev_result = result
+    M.floating_buf, M.floating_win = create_floating_file(result, opts)
+  end
+end
+
+function M.open_file()
+  -- Get the file currently open in the floating window
+  local filepath = vim.fn.expand "%:."
+
+  if not filepath then
+    print "peek: Unable to open the file!"
+    return
+  end
+
+  -- Close the floating window
+  pcall(vim.api.nvim_win_close, M.floating_win, true)
+
+  -- Edit the file
+  vim.cmd("edit " .. filepath)
+
+  local winnr = vim.api.nvim_get_current_win()
+
+  -- Set the cursor at the right position
+  M.set_cursor_to_prev_pos(winnr)
+end
+
+function M.set_cursor_to_prev_pos(winnr)
+  -- Get position of the thing to peek at
+  local location = M.prev_result
+  local range = location.targetRange or location.range
+  local cursor_pos = { range.start.line + 1, range.start.character }
+
+  -- Set the winnr to the floting window if none was passed in
+  winnr = winnr or M.floating_win
+  -- Set the cursor at the correct position in the floating window
+  vim.api.nvim_win_set_cursor(winnr, cursor_pos)
+end
+
+function M.Peek(what)
+  -- If a window already exists, focus it at the right position!
+  if vim.tbl_contains(vim.api.nvim_list_wins(), M.floating_win) then
+    local success_1, _ = pcall(vim.api.nvim_set_current_win, M.floating_win)
+    if not success_1 then
+      print "peek: You cannot edit the current file in a preview!"
+      return
+    end
+
+    -- Set the cursor at the correct position in the floating window
+    M.set_cursor_to_prev_pos()
+
+    vim.api.nvim_buf_set_keymap(
+      M.floating_buf,
+      "n",
+      "<CR>",
+      ":lua require('lsp.peek').open_file()<CR>",
+      { noremap = true, silent = true }
+    )
+  else
+    -- Make a new request and then create the new window in the callback
+    local params = vim.lsp.util.make_position_params()
+    local success, _ = pcall(vim.lsp.buf_request, 0, "textDocument/" .. what, params, preview_location_callback)
+    if not success then
+      print(
+        'peek: Error calling LSP method "textDocument/' .. what .. '". The current language lsp might not support it.'
+      )
+    end
+  end
+end
+
+return M