jaredly / reason-language-server

A language server for reason, in reason
MIT License
658 stars 84 forks source link

Feature request: support esy within docker #264

Open donut opened 5 years ago

donut commented 5 years ago

I have my esy environment setup in a docker container. I do not have it installed on the host machine. I use docker-compose exec <container name> esy. But I keep getting this error: "Couldn't get esy version".

The solution I found is to install esy on the host machine and build there and on the docker container. This isn't ideal, but it's working for now..

Unfortunately, this basically blocks all useful functionality.

debug log

Hello - from /Users/donut/.vscode/extensions/jaredly.reason-vscode-1.5.2/bin.native
Previous log location: /var/folders/64/z8mfxtld7fgdcsqx385q8vwm0000gn/T/lsp.log
Sending notification {"jsonrpc": "2.0", "method": "client/registerCapability", "params": {"registrations": [{"id": "watching", "method": "workspace/didChangeWatchedFiles", "registerOptions": {"watchers": [{"globPattern": "**/bsconfig.json", "globPattern": "**/.merlin"}]}}]}}
Sending response {"id": 0, "jsonrpc": "2.0", "result": {"capabilities": {"textDocumentSync": 1, "hoverProvider": true, "completionProvider": {"resolveProvider": true, "triggerCharacters": ["."]}, "signatureHelpProvider": {"triggerCharacters": ["("]}, "definitionProvider": true, "typeDefinitionProvider": true, "referencesProvider": true, "documentSymbolProvider": true, "codeActionProvider": true, "executeCommandProvider": {"commands": ["reason-language-server.add_to_interface_inner"]}, "codeLensProvider": {"resolveProvider": true}, "documentHighlightProvider": true, "documentRangeFormattingProvider": true, "documentFormattingProvider": true, "documentFormattingProvider": true, "renameProvider": true}}}
Read message 
{"jsonrpc":"2.0","method":"initialized","params":{}}
Read message 
{"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"reason_language_server":{"location":"","build_system_override_by_root":{},"refmt":"","lispRefmt":"","format_width":"80","per_value_codelens":true,"dependencies_codelens":true,"opens_codelens":true,"show_module_path_on_hover":true,"reloadOnChange":false,"show_debug_errors":false,"autoRebuild":true}}}}
Read message 
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///Users/donut/RTM/rtm.dev/services/ads-txt/bin/main.ml","languageId":"ocaml","version":1,"text":"\nopen Lwt\nopen Cohttp\nopen Cohttp_lwt_unix\n\nlet freestar_adstxt_url = \n  \"https://storage.googleapis.com/a.pub.network/core/ads.txt\"\n\nlet body () =\n  Client.get (Uri.of_string freestar_adstxt_url) >>= fun (resp, body) ->\n  let code = resp |> Response.status |> Code.code_of_status in\n  Printf.printf \"Response code: %d\\n\" code;\n  Printf.printf \"Headers: %s\\n\" (resp |> Response.headers |> Header.to_string);\n  body |> Cohttp_lwt.Body.to_string >|= fun body ->\n  Printf.printf \"Body of length: %d\\n\" (String.length body);\n  body\n\nlet () =\n  let body = Lwt_main.run body in\n  print_endline (\"Received body\\n\" ^ body)\n"}}}
Found a `dune` file at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
]] Making a new jbuilder package at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
=== Project root: /Users/donut/RTM/rtm.dev/services/ads-txt
Sending notification {"jsonrpc": "2.0", "method": "window/showMessage", "params": {"type": 1, "message": "Couldn't get esy version"}}
Found a `dune` file at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
]] Making a new jbuilder package at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
=== Project root: /Users/donut/RTM/rtm.dev/services/ads-txt
Read message 
{"jsonrpc":"2.0","id":1,"method":"textDocument/documentSymbol","params":{"textDocument":{"uri":"file:///Users/donut/RTM/rtm.dev/services/ads-txt/bin/main.ml"}}}
[server] Got a method textDocument/documentSymbol
[server] processing took 0.0109672546387ms
Found a `dune` file at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
]] Making a new jbuilder package at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
=== Project root: /Users/donut/RTM/rtm.dev/services/ads-txt
Sending response {"id": 1, "jsonrpc": "2.0", "error": {"code": -32603, "message": "Couldn't get esy version"}}
Read message 
{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/donut/RTM/rtm.dev/services/ads-txt/bin/main.ml"},"range":{"start":{"line":19,"character":22},"end":{"line":19,"character":22}},"context":{"diagnostics":[]}}}
[server] Got a method textDocument/codeAction
[server] processing took 0.0350475311279ms
Found a `dune` file at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
]] Making a new jbuilder package at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
=== Project root: /Users/donut/RTM/rtm.dev/services/ads-txt
Sending response {"id": 2, "jsonrpc": "2.0", "error": {"code": -32603, "message": "Couldn't get esy version"}}
Read message 
{"jsonrpc":"2.0","id":3,"method":"textDocument/codeLens","params":{"textDocument":{"uri":"file:///Users/donut/RTM/rtm.dev/services/ads-txt/bin/main.ml"}}}
[server] Got a method textDocument/codeLens
[server] processing took 0.0109672546387ms
Found a `dune` file at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
]] Making a new jbuilder package at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
=== Project root: /Users/donut/RTM/rtm.dev/services/ads-txt
Sending response {"id": 3, "jsonrpc": "2.0", "result": [{"range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}, "command": {"title": "Unable to load compilation data: Couldn't get esy version", "command": ""}}]}
Read message 
{"jsonrpc":"2.0","id":4,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/donut/RTM/rtm.dev/services/ads-txt/bin/main.ml"},"range":{"start":{"line":11,"character":42},"end":{"line":11,"character":42}},"context":{"diagnostics":[]}}}
[server] Got a method textDocument/codeAction
[server] processing took 0.00596046447754ms
Found a `dune` file at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
]] Making a new jbuilder package at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
=== Project root: /Users/donut/RTM/rtm.dev/services/ads-txt
Sending response {"id": 4, "jsonrpc": "2.0", "error": {"code": -32603, "message": "Couldn't get esy version"}}
Read message 
{"jsonrpc":"2.0","method":"$/cancelRequest","params":{"id":2}}
Read message 
{"jsonrpc":"2.0","id":5,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///Users/donut/RTM/rtm.dev/services/ads-txt/bin/main.ml"},"position":{"line":19,"character":35}}}
[server] Got a method textDocument/hover
[server] processing took 0.0100135803223ms
Found a `dune` file at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
]] Making a new jbuilder package at /Users/donut/RTM/rtm.dev/services/ads-txt/bin
=== Project root: /Users/donut/RTM/rtm.dev/services/ads-txt
Sending response {"id": 5, "jsonrpc": "2.0", "error": {"code": -32603, "message": "Couldn't get esy version"}}
Read message 
{"jsonrpc":"2.0","method":"$/cancelRequest","params":{"id":5}}
Read message 
{"jsonrpc":"2.0","method":"$/setTraceNotification","params":{"value":"off"}}
Read message 
{"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"reason_language_server":{"location":"","build_system_override_by_root":{},"refmt":"","lispRefmt":"","format_width":"80","per_value_codelens":true,"dependencies_codelens":true,"opens_codelens":true,"show_module_path_on_hover":true,"reloadOnChange":false,"show_debug_errors":false,"autoRebuild":true}}}}
Read message 
{"jsonrpc":"2.0","method":"$/setTraceNotification","params":{"value":"off"}}
Read message 
{"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"reason_language_server":{"location":"","build_system_override_by_root":{},"refmt":"","lispRefmt":"","format_width":"80","per_value_codelens":true,"dependencies_codelens":true,"opens_codelens":true,"show_module_path_on_hover":true,"reloadOnChange":false,"show_debug_errors":false,"autoRebuild":true}}}}
jaredly commented 5 years ago

Hmm yeah that's a tricky one. reason-language-server needs to be able to communicate with esy to determine the location of ocamlc & other binaries used for on-the-fly compilation, and to read the compiler artifacts for analysis. The only solution I could imagine would be having the reason-language-server be located in the docker container, and have the editor extension run it in there. Having a setup like this would also unlock WSL compatibility, which some people have asked for (#114).

To try out this proof of concept, you could have something like the following:

And I think that might work? Let me know how it goes :)

donut commented 5 years ago

Unfortunately, that didn't work. I'm getting this error:

the input device is not a TTY [Info - 8:25:21 AM] Connection to server got closed. Server will restart

I used the linux.zip file from the releases page and placed the exe at [/reason-language-server/rls.exe] in the container. The bash script used (rls.exe):

#!/bin/bash

export $(grep -v ^# /Users/donut/RTM/OVPSync/.env | xargs -0)
docker-compose --file="/Users/donut/RTM/OVPSync/docker-compose.yml" exec dev \
  /reason-language-server/rls.exe "$@"

Then I set reason_language_server.location to rls.exe in the Workspace Settings. I put the bash script in the project's root.

The Dockerfile:

ARG ESY_IMAGE
FROM $ESY_IMAGE as esy

# Niceties
RUN apk add --no-cache fish vim

# Timezone
# See https://serverfault.com/q/683605/54523
ARG TZ
RUN apk add --no-cache tzdata
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# Needed for @esy-ocaml/libffi@3.2.10
RUN apk add --no-cache texinfo
# Needed for @opam/tls
RUN apk add --no-cache gmp-dev
# Need for @opam/caqti-driver-mariadb
RUN apk add --no-cache mariadb-dev

# Reason language server
# Used by VSCode through `docker-compose exec` to get the right environment.
RUN apk add --no-cache unzip
RUN mkdir /reason-language-server
WORKDIR /reason-language-server
RUN curl -LO https://github.com/jaredly/reason-language-server/releases/download/1.5.2/linux.zip
RUN unzip linux.zip
RUN rm linux.zip
RUN mv reason-language-server/reason-language-server.exe rls.exe
RUN rmdir reason-language-server
RUN apk del --no-cache unzip

RUN mkdir /app
WORKDIR /app

ENTRYPOINT ["tail", "-f", "/dev/null"]

On the host, in the project's directory, running using the bash script:

[I] ~/RTM/OVPSync (dockerificiation *$%)
↪  ./rls.exe --help                                                                                               0@08:51:37

🎉 Reason Language Server 🎉

Usage: run without arguments, and communicate over stdin/stdout,
following the language server protocol as defined in
https://microsoft.github.io/language-server-protocol/specification

Logs are stored in `<project_root>/node_modules/.lsp/debug.log`.
jaredly commented 5 years ago

Looks like the Input device is not a TTY error is coming from docker. (see this SO answer https://stackoverflow.com/a/43099210/290784) I assume docker-compose passes in -t automatically. Here's an issue on the compose repo from a year ago https://github.com/docker/compose/issues/5696 that suggests putting export COMPOSE_INTERACTIVE_NO_CLI=1 in your bash script ... and you might also need the -T flag in your docker compose command.

donut commented 5 years ago

Progress, I think.

With just adding -T, I got this new error:

Setting log location: /Users/donut/RTM/OVPSync/node_modules/.lsp/debug.log
[Error - 3:06:14 PM] Request textDocument/documentSymbol failed.
  Message: No root directory found
  Code: -32603 
[Error - 3:06:21 PM] Request textDocument/documentHighlight failed.
  Message: No root directory found
  Code: -32603 
[Error - 3:06:22 PM] Request textDocument/codeAction failed.
  Message: No root directory found
  Code: -32603 

If I include export COMPOSE_INTERACTIVE_NO_CLI=1, I get no error messages or any output for Reason Language Server, but also nothing works for .ml files except syntax highlighting. This happens regardless of the -T flag's presence.

Here's the updated shell script:

#!/bin/bash

export $(grep -v '^#' /Users/donut/RTM/OVPSync/.env | xargs -0)

docker-compose --file="/Users/donut/RTM/OVPSync/docker-compose.yml" \
  exec -T --workdir=/app app /reason-language-server/rls.exe $@

The Dockerfile hasn't changed in any ways relevant to this.

jaredly commented 5 years ago

Very cool. Ok yeah so the next issue is helping rls translate between the directory paths that vscode is sending (which are in the host fs) and the ones that it can access (in the docker image). I imagine this would want to be done as an argument to rls in the bash script. I don't have a ton of bandwidth to implement this at the moment, but if you're interested I could give you pointers!

donut commented 5 years ago

Yeah, I'm interested! If you can point me in the right direction, I'll take a look on Monday.