Support filename mapping for language servers
Hello! 👋
The setup: I am integrating a language server that runs remotely. Ale is able to read/write to the server over a TCP socket.
The hiccup: How should Ale handle different project roots for the client and server?
I have the linter defined as:
call ale#linter#Define('<language>', {
\ 'name': '<linter-name>',
\ 'lsp': 'socket',
\ 'address': 'localhost:7777',
\ 'language': '<language>',
\ 'project_root': '<path_to_server_project_root>',
\})
The outgoing requests are good, but the diagnostic file paths are returned from the server's point of view, e.g.:
"uri":"file://<path_to_server_project_root>/<relative_file_name>"
These file URIs obviously mean nothing to Ale.
Guidance How should this be handled? I'm not sure if the LS protocol provides a way for the server to know the client's project root, so I figured we could try handling it client-side. I was able to get diagnostics working by patching https://github.com/w0rp/ale/blob/fcc2c3ba71afa2a7965f3c1e9ec8c03381178180/autoload/ale/path.vim#L199-L201 to truncate everything up until <relative_file_name>. I was thinking that this could be done dynamically by truncating len('file://') + len(project_root). Is this a reasonable approach? Are there better approaches?
Thanks!
Note: If Ale sent rootUri and rootPath to the server in the initialize message, then this specific language server would handle translating between server-local and client-local file URIs (https://microsoft.github.io/language-server-protocol/specification#initialize).
ALE does send rootUri in the initialize message.
It sounds like we'll have to add a setting for mapping server paths to client paths, like so:
let b:ale_lsp_mapped_paths = {
\ 'linter_name': {'/server/path/': '/client/path/'},
\}
I have found a second issue, different from client/server filepaths (let me know if we should discuss in a new issue).
The setup is:
- Vim is opened at
~/src/repo - File
a.rbis opened:ls 1 %a "a.rb" line 1
If diagnostics come back for the URI file:///path/to/home/src/repo/a.rb, ale does not recognize this as the open buffer because of the ^$:
https://github.com/w0rp/ale/blob/6c47d7fc352659cd2dc869a9a46a04a8492fc829/autoload/ale/lsp_linter.vim#L37
bufnr('^a.rb$') properly returns 1, but bufnr('^/path/to/home/src/repo/a.rb$') returns -1. (Interestingly, bufnr('/path/to/home/src/repo/a.rb') without the start/end anchors returns 1.)
As an option, we could perform a left trim of the project root (/path/to/home/src/repo) before calling the anchored bufnr on the file. What do you think of this approach?
It sounds like a problem that would also be solved by the solution I suggested above. My suggestion is to eventually implement an option for converting paths back and forth between Vim and external programs. The same option could also be used for implementing Docker support.
I have implemented #2556 now, which will partially resolve this issue for language servers. Some additional code will likely be needed to map the project_root, and the filenames that are returned by the remote servers.
Possibly related: #3492 #3805