zed-industries / zed

Code at the speed of thought – Zed is a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter.
https://zed.dev
Other
46.88k stars 2.69k forks source link

Provide additional language server support for Elixir #4732

Open scottming opened 1 year ago

scottming commented 1 year ago

Check for existing issues

Language

elixir

Tree Sitter parser link

https://github.com/elixir-lang/tree-sitter-elixir

Language server link

https://github.com/lexical-lsp/lexical

Misc notes

Hello Zed team,

I have tried out Zed and found that it opens quickly and provides a great user experience.

I want to suggest adding an option for custom LS. As a committer of Lexical, I mainly use it for developing Elixir projects. It is speedy and has many unique features, such as Real-time diagnostics support, Context-aware code completion, Consistent and Stable Formatting, etc.

Welcome to check out our README and see our design philosophy.

FYI, for neovim config, I just need to:

local lspconfig = require("lspconfig")
local configs = require("lspconfig.configs")

local M = {
    filetypes = { "elixir", "eelixir", "heex", "surface" },
    cmd = { "/Users/scottming/Code/lexical/_build/dev/rel/lexical/start_lexical.sh" },
    settings = {},
}

function M.load_lexical()
    if not configs.lexical then
        configs.lexical = {
            default_config = {
                filetypes = M.filetypes,
                root_dir = function(fname)
                    local project = lspconfig.util.root_pattern(".git")(fname)
                    return lspconfig.util.root_pattern("mix.exs", ".git")(fname) or vim.loop.os_homedir()
                end,
            },
        }
    end
end

return M
mikayla-maki commented 11 months ago

Hi y'all,

We don't fully support lexical yet, but in the meantime the latest version in Zed preview (v0.106.2) supports a new settings field which could be used to provide access to the lexical LSP, as long as it communicates over standard in and out. An analoguous setting to the above would be:

{
// Other settings fields
  "elixir": {
    "lsp": {
       "local": {
            "path": "/Users/scottming/Code/lexical/_build/dev/rel/lexical/start_lexical.sh",
            "arguments": [""]
        }
     }
  }
}

Let me know if there's any trouble getting it to work :)

scottming commented 11 months ago

Hello, @mikayla-maki Thanks for the team's work. I tried Zed through the branch of this PR, and I found no issues with diagnostics, format, hover, or goto definition.

However, there are some problems with autocompletion. For example, when entering a module name after an alias, like: alias Le|(| is the cursor), the module cannot be autocompleted correctly. Additionally, autocompletion does not work for submodules after a dot in a module, like Lexical.|

I checked the backend logs and found that the Zed client is not sending the correct request to Lexical. So, this might be a bug in the client. However, I don't know how to view the client's request logs. Can you please guide me on how to do that?

mikayla-maki commented 11 months ago

Hi Scott,

You can look at the LSP logs through the debug: open language server logs action. We mainly use this internally for debugging these kinds of issues so it's a little rougher than the rest of our UI. Once in that screen you can click the LSP name up in the toolbar and click on 'RPC messages' for the local LSP server. That will intercept and save all RPC messages from that point on and you can go trigger the problem in another file. Hopefully that helps debug the issue, and please let me know if you find issues with our LSP support! I've been fixing a couple places where we're off spec for NextLS already :). Glad to hear it's mostly working well!

scottming commented 11 months ago

Hi, Mikayla @mikayla-maki I clicked and checked the box, but still can't see the request log

CleanShot 2023-09-30 at 11 48 39@2x
mikayla-maki commented 11 months ago

You might need to also click on the 'RPC messages' text, does that show the logs? Like I said, mainly intended to be an internal UI 😅

scottming commented 11 months ago

You might need to also click on the 'RPC messages' text, does that show the logs? Like I said, mainly intended to be an internal UI 😅

I see, I can see those messages right now.

My guess should be accurate. When I type "alias Le", Zed indeed did not send any autocomplete-related requests.

// Send:
{"jsonrpc":"2.0","id":836,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs"},"position":{"line":0,"character":7}}}
// Receive:
{"id":836,"jsonrpc":"2.0","result":null}
// Send:
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs","version":234},"contentChanges":[{"range":{"start":{"line":9,"character":2},"end":{"line":9,"character":2}},"text":"a"}]}}
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs","version":235},"contentChanges":[{"range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}},"text":"l"}]}}
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs","version":236},"contentChanges":[{"range":{"start":{"line":9,"character":4},"end":{"line":9,"character":4}},"text":"i"}]}}
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs","version":237},"contentChanges":[{"range":{"start":{"line":9,"character":5},"end":{"line":9,"character":5}},"text":"a"}]}}
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs","version":238},"contentChanges":[{"range":{"start":{"line":9,"character":6},"end":{"line":9,"character":6}},"text":"s"}]}}
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs","version":239},"contentChanges":[{"range":{"start":{"line":9,"character":7},"end":{"line":9,"character":7}},"text":" "}]}}
{"jsonrpc":"2.0","id":837,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs"},"range":{"start":{"line":18,"character":0},"end":{"line":18,"character":0}},"context":{"diagnostics":[],"only":["","quickfix","refactor","refactor.extract","source"]}}}
{"jsonrpc":"2.0","id":838,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs"},"range":{"start":{"line":9,"character":8},"end":{"line":9,"character":8}},"context":{"diagnostics":[{"range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}},"severity":4,"message":""}],"only":["","quickfix","refactor","refactor.extract","source"]}}}
// Receive:
{"id":837,"jsonrpc":"2.0","result":[]}
{"id":838,"jsonrpc":"2.0","result":[]}
// Send:
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs","version":240},"contentChanges":[{"range":{"start":{"line":9,"character":8},"end":{"line":9,"character":8}},"text":"L"}]}}
// Receive:
{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"diagnostics":[],"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/node_test.exs","version":null}}
{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"diagnostics":[{"message":"undefined variable \"alias\"","range":{"end":{"character":0,"line":10},"start":{"character":2,"line":9}},"severity":1,"source":"Elixir"}],"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs","version":null}}
{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"diagnostics":[],"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/node_test.exs","version":null}}
{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"diagnostics":[{"message":"There should be no trailing white-space at the end of a line.","range":{"end":{"character":0,"line":10},"start":{"character":7,"line":9}},"severity":4,"source":"Credo"},{"message":"undefined variable \"alias\"","range":{"end":{"character":0,"line":10},"start":{"character":2,"line":9}},"severity":1,"source":"Elixir"}],"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs","version":null}}
// Send:
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs","version":241},"contentChanges":[{"range":{"start":{"line":9,"character":9},"end":{"line":9,"character":9}},"text":"e"}]}}
{"jsonrpc":"2.0","id":839,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs"},"range":{"start":{"line":18,"character":0},"end":{"line":18,"character":0}},"context":{"diagnostics":[],"only":["","quickfix","refactor","refactor.extract","source"]}}}
{"jsonrpc":"2.0","id":840,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs"},"range":{"start":{"line":9,"character":10},"end":{"line":9,"character":10}},"context":{"diagnostics":[{"range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}},"severity":1,"message":""},{"range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}},"severity":4,"message":""}],"only":["","quickfix","refactor","refactor.extract","source"]}}}
// Receive:
{"id":839,"jsonrpc":"2.0","result":[]}
{"id":840,"jsonrpc":"2.0","result":[]}
{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"diagnostics":[],"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/node_test.exs","version":null}}
{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"diagnostics":[],"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs","version":null}}
{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"diagnostics":[],"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/node_test.exs","version":null}}
{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"diagnostics":[{"message":"The alias `Lexical.Test.DispatchFake` is not alphabetically ordered among its group.","range":{"end":{"character":0,"line":9},"start":{"character":8,"line":8}},"severity":4,"source":"Credo"}],"uri":"file:///Users/scottming/Code/lexical/apps/server/test/lexical/server/project/progress_test.exs","version":null}}
CleanShot 2023-09-30 at 12 03 38@2x
mikayla-maki commented 11 months ago

Hmmm, maybe we could debug this live in the Zed discord?

ArthurMmn commented 10 months ago

tldr: when using a "local lsp" for elixir, is it possible to get the same lsp working for heex files instead of ElixirLS ?

Hi everyone, sorry for re-opening the conversation.

I have been recentely testing the different elixir LSP for the simple reason that Elixir LS feels very slow on my mac and on my huge work project (intel mac from 2019). Resulting in almost no LSP functionnality being available as the lsp spend most of its time re-compiling.

(side-note, as I'm not sure how things works : who is responsible for the lsp task and responsiveness ? I guess the lsp speed is editor agnostic but depending on which task are asked the LSP messages are faster or slower ?)

But back on topic, I'm trying lexical through the local config Mikayla gave us and it work great for .ex files ! (Maybe the lack of features means that it's compiling faster ? 😁) My only issue is that for .heex files, the LSP used is still ElixirLS, is there an option to use the same lsp for heex and ex files ?