summaryrefslogtreecommitdiff
path: root/.config/nvim/lua/lsp/peek.lua
blob: cc8e57a9063e17667479a2214aacd4043bcad022 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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 floating 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