aboutsummaryrefslogtreecommitdiffstats
path: root/lua/common_lsp/gopls.lua
blob: 96e9df761d73471d4fe90f6553826607c3a86108 (plain) (blame)
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
local util = require 'common_lsp/util'
local api, validate, lsp = vim.api, vim.validate, vim.lsp

local M = {}

M.name = "gopls"

local default_config
default_config = {
  name = M.name;
  cmd = {"gopls"};
  filetype = {"go"};
  root_dir = util.root_pattern("go.mod", ".git");
  log_level = lsp.protocol.MessageType.Warning;
  settings = {};
}

local function setup_callbacks(config)
  config.callbacks = config.callbacks or {}

  config.callbacks["window/logMessage"] = function(err, method, params, client_id)
    if params and params.type <= config.log_level then
      lsp.builtin_callbacks[method](err, method, params, client_id)
    end
  end

  config.callbacks["workspace/configuration"] = function(err, method, params, client_id)
    if err then error(tostring(err)) end
    if not params.items then
      return {}
    end

    local result = {}
    for _, item in ipairs(params.items) do
      if item.section then
        local value = util.lookup_section(config.settings, item.section) or vim.NIL
        -- Uncomment this to debug.
        -- print(string.format("config[%q] = %s", item.section, inspect(value)))
        table.insert(result, value)
      end
    end
    return result
  end
end

-- A function to set up `gopls` easier.
--
-- Additionally, it sets up the following commands:
-- - SKELETON_SPOOKY_COMMAND: This does something SPOOKY.
--
-- {config} is the same as |vim.lsp.add_filetype_config()|, but with some
-- additions and changes:
--
-- {root_dir}
--   REQUIRED function(filename, bufnr) which is called on new candidate
--   buffers to attach to and returns either a root_dir or nil.
--   If a root_dir is returned, then this file will also be attached. You can
--   optionally use {filetype} to help pre-filter by filetype.
--   If a root_dir is returned which differs from any previously returned
--   root_dir, a new server will be spawned with that root_dir.
--   If nil is returned, the buffer is skipped.

--   See |common_lsp.util.search_ancestors()| and the functions which use it:
--   - |common_lsp.util.root_pattern(patterns...)| finds an ancestor which a
--   descendent which has one of the files in `patterns...`. This is equivalent
--   to coc.nvim's "rootPatterns"
--   - More specific utilities:
--     - |common_lsp.util.find_git_root()|
--     - |common_lsp.util.find_node_modules_root()|
--     - |common_lsp.util.find_package_json_root()|
--
--   Defaults to common_lsp.util.root_pattern("go.mod", ".git")
--
-- {name}
--   Defaults to "gopls"
--
-- {cmd}
--   Defaults to {"gopls"}
--
-- {filetype}
--   Defaults to {"go"}. This is optional and only serves to reduce the scope
--   of files to filter for {root_dir}.
--
-- {log_level}
--   controls the level of logs to show from build processes and other
--   window/logMessage events. By default it is set to
--   vim.lsp.protocol.MessageType.Warning instead of
--   vim.lsp.protocol.MessageType.Log.
--
-- {settings}
--   This is a table, and the keys are case sensitive.
--   Example: `settings = { }`
function M.setup(config)
  validate {
    root_dir = {config.root_dir, 'f'};
    filetype = {config.filetype, 't', true};
  }

  local filetype = config.filetype or default_config.filetype

  if filetype then
    local filetypes
    if type(filetype) == 'string' then
      filetypes = { filetype }
    else
      filetypes = filetype
    end
    api.nvim_command(string.format(
        "autocmd FileType %s lua require'common_lsp'[%q].manager.try_add()"
        , table.concat(filetypes, ',')
        , M.name
        ))
  else
    api.nvim_command(string.format(
        "autocmd BufReadPost * lua require'common_lsp'[%q].manager.try_add()"
        , M.name
        ))
  end

  local get_root_dir = config.root_dir or default_config.root_dir

  M.manager = util.server_per_root_dir_manager(function(_root_dir)
    local new_config = vim.tbl_extend("keep", config, default_config)
    -- Deepcopy anything that is >1 level nested.
    new_config.settings = vim.deepcopy(new_config.settings)
    util.tbl_deep_extend(new_config.settings, default_config.settings)

    new_config.capabilities = new_config.capabilities or lsp.protocol.make_client_capabilities()
    util.tbl_deep_extend(new_config.capabilities, {
      workspace = {
        configuration = true;
      }
    })

    setup_callbacks(new_config)

    new_config.on_attach = util.add_hook_after(new_config.on_attach, function(client, bufnr)
      if bufnr == api.nvim_get_current_buf() then
        M._setup_buffer()
      else
        api.nvim_command(string.format(
            "autocmd BufEnter <buffer=%d> ++once lua require'common_lsp/%s'._setup_buffer()",
            M.name,
            bufnr))
      end
    end)
    return new_config
  end)

  function M.manager.try_add()
    local root_dir = get_root_dir(api.nvim_buf_get_name(0), api.nvim_get_current_buf())
    print(api.nvim_get_current_buf(), root_dir)
    local id = M.manager.add(root_dir)
    lsp.buf_attach_client(0, id)
  end
end

-- Declare any commands here. You can use additional modifiers like "-range"
-- which will be added as command options. All of these commands are buffer
-- level by default.
M.commands = {
  SKELETON_SPOOKY_COMMAND = {
    function()
      local bufnr = util.validate_bufnr(0)
      print("SPOOKY COMMAND STUFF!", bufnr)
    end;
  };
}

function M._setup_buffer()
  -- Create the module commands
  util.create_module_commands(M.name, M.commands)
end

return M
-- vim:et ts=2 sw=2