lsp-extension.txt 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. *lsp-extension.txt* LSP Extension
  2. NVIM REFERENCE MANUAL
  3. The `vim.lsp` Lua module is a framework for building LSP plugins.
  4. 1. Start with |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()|.
  5. 2. Peek at the API: >
  6. :lua print(vim.inspect(vim.lsp))
  7. < 3. See |lsp-extension-example| for a full example.
  8. ================================================================================
  9. LSP EXAMPLE *lsp-extension-example*
  10. This example is for plugin authors or users who want a lot of control. If you
  11. are just getting started see |lsp-quickstart|.
  12. For more advanced configurations where just filtering by filetype isn't
  13. sufficient, you can use the `vim.lsp.start_client()` and
  14. `vim.lsp.buf_attach_client()` commands to easily customize the configuration
  15. however you please. For example, if you want to do your own filtering, or
  16. start a new LSP client based on the root directory for working with multiple
  17. projects in a single session. To illustrate, the following is a fully working
  18. Lua example.
  19. The example will:
  20. 1. Check for each new buffer whether or not we want to start an LSP client.
  21. 2. Try to find a root directory by ascending from the buffer's path.
  22. 3. Create a new LSP for that root directory if one doesn't exist.
  23. 4. Attach the buffer to the client for that root directory.
  24. >
  25. -- Some path manipulation utilities
  26. local function is_dir(filename)
  27. local stat = vim.loop.fs_stat(filename)
  28. return stat and stat.type == 'directory' or false
  29. end
  30. local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/"
  31. -- Assumes filepath is a file.
  32. local function dirname(filepath)
  33. local is_changed = false
  34. local result = filepath:gsub(path_sep.."([^"..path_sep.."]+)$", function()
  35. is_changed = true
  36. return ""
  37. end)
  38. return result, is_changed
  39. end
  40. local function path_join(...)
  41. return table.concat(vim.tbl_flatten {...}, path_sep)
  42. end
  43. -- Ascend the buffer's path until we find the rootdir.
  44. -- is_root_path is a function which returns bool
  45. local function buffer_find_root_dir(bufnr, is_root_path)
  46. local bufname = vim.api.nvim_buf_get_name(bufnr)
  47. if vim.fn.filereadable(bufname) == 0 then
  48. return nil
  49. end
  50. local dir = bufname
  51. -- Just in case our algorithm is buggy, don't infinite loop.
  52. for _ = 1, 100 do
  53. local did_change
  54. dir, did_change = dirname(dir)
  55. if is_root_path(dir, bufname) then
  56. return dir, bufname
  57. end
  58. -- If we can't ascend further, then stop looking.
  59. if not did_change then
  60. return nil
  61. end
  62. end
  63. end
  64. -- A table to store our root_dir to client_id lookup. We want one LSP per
  65. -- root directory, and this is how we assert that.
  66. local javascript_lsps = {}
  67. -- Which filetypes we want to consider.
  68. local javascript_filetypes = {
  69. ["javascript.jsx"] = true;
  70. ["javascript"] = true;
  71. ["typescript"] = true;
  72. ["typescript.jsx"] = true;
  73. }
  74. -- Create a template configuration for a server to start, minus the root_dir
  75. -- which we will specify later.
  76. local javascript_lsp_config = {
  77. name = "javascript";
  78. cmd = { path_join(os.getenv("JAVASCRIPT_LANGUAGE_SERVER_DIRECTORY"), "lib", "language-server-stdio.js") };
  79. }
  80. -- This needs to be global so that we can call it from the autocmd.
  81. function check_start_javascript_lsp()
  82. local bufnr = vim.api.nvim_get_current_buf()
  83. -- Filter which files we are considering.
  84. if not javascript_filetypes[vim.api.nvim_buf_get_option(bufnr, 'filetype')] then
  85. return
  86. end
  87. -- Try to find our root directory. We will define this as a directory which contains
  88. -- node_modules. Another choice would be to check for `package.json`, or for `.git`.
  89. local root_dir = buffer_find_root_dir(bufnr, function(dir)
  90. return is_dir(path_join(dir, 'node_modules'))
  91. -- return vim.fn.filereadable(path_join(dir, 'package.json')) == 1
  92. -- return is_dir(path_join(dir, '.git'))
  93. end)
  94. -- We couldn't find a root directory, so ignore this file.
  95. if not root_dir then return end
  96. -- Check if we have a client already or start and store it.
  97. local client_id = javascript_lsps[root_dir]
  98. if not client_id then
  99. local new_config = vim.tbl_extend("error", javascript_lsp_config, {
  100. root_dir = root_dir;
  101. })
  102. client_id = vim.lsp.start_client(new_config)
  103. javascript_lsps[root_dir] = client_id
  104. end
  105. -- Finally, attach to the buffer to track changes. This will do nothing if we
  106. -- are already attached.
  107. vim.lsp.buf_attach_client(bufnr, client_id)
  108. end
  109. vim.api.nvim_command [[autocmd BufReadPost * lua check_start_javascript_lsp()]]
  110. <
  111. vim:tw=78:ts=8:ft=help:norl: