aboutsummaryrefslogtreecommitdiff
path: root/.config/nvim/lsp/julials.lua
blob: 949a7e83d171c6c984d012b48287383629d3a676 (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
---@brief
---
--- https://github.com/julia-vscode/julia-vscode
---
--- LanguageServer.jl can be installed with `julia` and `Pkg`:
--- ```sh
--- julia --project=~/.julia/environments/nvim-lspconfig -e 'using Pkg; Pkg.add("LanguageServer")'
--- ```
--- where `~/.julia/environments/nvim-lspconfig` is the location where
--- the default configuration expects LanguageServer.jl to be installed.
---
--- To update an existing install, use the following command:
--- ```sh
--- julia --project=~/.julia/environments/nvim-lspconfig -e 'using Pkg; Pkg.update()'
--- ```
---
--- Note: In order to have LanguageServer.jl pick up installed packages or dependencies in a
--- Julia project, you must make sure that the project is instantiated:
--- ```sh
--- julia --project=/path/to/my/project -e 'using Pkg; Pkg.instantiate()'
--- ```
---
--- Note: The julia programming language searches for global environments within the `environments/`
--- folder of `$JULIA_DEPOT_PATH` entries. By default this simply `~/.julia/environments`

local root_files = { 'Project.toml', 'JuliaProject.toml' }

local function activate_env(path)
  assert(vim.fn.has 'nvim-0.10' == 1, 'requires Nvim 0.10 or newer')
  local bufnr = vim.api.nvim_get_current_buf()
  local julials_clients = vim.lsp.get_clients { bufnr = bufnr, name = 'julials' }
  assert(
    #julials_clients > 0,
    'method julia/activateenvironment is not supported by any servers active on the current buffer'
  )
  local function _activate_env(environment)
    if environment then
      for _, julials_client in ipairs(julials_clients) do
        julials_client:notify('julia/activateenvironment', { envPath = environment })
      end
      vim.notify('Julia environment activated: \n`' .. environment .. '`', vim.log.levels.INFO)
    end
  end
  if path then
    path = vim.fs.normalize(vim.fn.fnamemodify(vim.fn.expand(path), ':p'))
    local found_env = false
    for _, project_file in ipairs(root_files) do
      local file = vim.uv.fs_stat(vim.fs.joinpath(path, project_file))
      if file and file.type then
        found_env = true
        break
      end
    end
    if not found_env then
      vim.notify('Path is not a julia environment: \n`' .. path .. '`', vim.log.levels.WARN)
      return
    end
    _activate_env(path)
  else
    local depot_paths = vim.env.JULIA_DEPOT_PATH
        and vim.split(vim.env.JULIA_DEPOT_PATH, vim.fn.has 'win32' == 1 and ';' or ':')
      or { vim.fn.expand '~/.julia' }
    local environments = {}
    vim.list_extend(environments, vim.fs.find(root_files, { type = 'file', upward = true, limit = math.huge }))
    for _, depot_path in ipairs(depot_paths) do
      local depot_env = vim.fs.joinpath(vim.fs.normalize(depot_path), 'environments')
      vim.list_extend(
        environments,
        vim.fs.find(function(name, env_path)
          return vim.tbl_contains(root_files, name) and string.sub(env_path, #depot_env + 1):match '^/[^/]*$'
        end, { path = depot_env, type = 'file', limit = math.huge })
      )
    end
    environments = vim.tbl_map(vim.fs.dirname, environments)
    vim.ui.select(environments, { prompt = 'Select a Julia environment' }, _activate_env)
  end
end

local cmd = {
  'julia',
  '--startup-file=no',
  '--history-file=no',
  '-e',
  [[
    # Load LanguageServer.jl: attempt to load from ~/.julia/environments/nvim-lspconfig
    # with the regular load path as a fallback
    ls_install_path = joinpath(
        get(DEPOT_PATH, 1, joinpath(homedir(), ".julia")),
        "environments", "nvim-lspconfig"
    )
    pushfirst!(LOAD_PATH, ls_install_path)
    using LanguageServer
    popfirst!(LOAD_PATH)
    depot_path = get(ENV, "JULIA_DEPOT_PATH", "")
    project_path = let
        dirname(something(
            ## 1. Finds an explicitly set project (JULIA_PROJECT)
            Base.load_path_expand((
                p = get(ENV, "JULIA_PROJECT", nothing);
                p === nothing ? nothing : isempty(p) ? nothing : p
            )),
            ## 2. Look for a Project.toml file in the current working directory,
            ##    or parent directories, with $HOME as an upper boundary
            Base.current_project(),
            ## 3. First entry in the load path
            get(Base.load_path(), 1, nothing),
            ## 4. Fallback to default global environment,
            ##    this is more or less unreachable
            Base.load_path_expand("@v#.#"),
        ))
    end
    @info "Running language server" VERSION pwd() project_path depot_path
    server = LanguageServer.LanguageServerInstance(stdin, stdout, project_path, depot_path)
    server.runlinter = true
    run(server)
  ]],
}

---@type vim.lsp.Config
return {
  cmd = cmd,
  filetypes = { 'julia' },
  root_markers = root_files,
  on_attach = function(_, bufnr)
    vim.api.nvim_buf_create_user_command(bufnr, 'LspJuliaActivateEnv', activate_env, {
      desc = 'Activate a Julia environment',
      nargs = '?',
      complete = 'file',
    })
  end,
}