Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to lookup Elixir source files under configured location #277

Merged
merged 2 commits into from
Aug 29, 2023

Conversation

smaximov
Copy link
Contributor

@smaximov smaximov commented Aug 28, 2023

Greetings!

elixir_sense uses mod.module_info(:compile)[:source] in order to retrieve the source file for a module. While it works for regular Elixir files and dependencies, it fails for the Elixir sources (i.e., for the applications like :elixir, :ex_unit, :eex that ship with Elixir) if Elixir was built in a sandboxed environment. That happens, for example, on NixOS as it returns a path to a build location that no longer exists:

iex(1)> String.module_info(:compile)[:source]
'/build/source/lib/elixir/lib/string.ex'

("/build/source" refers to a location that contained Elixir source while it was being built inside a sandbox.)

Despite that, on NixOS it's easy to install the Elixir source as a package and provide the path to it as an environment variable.

Click me to get NixOS-specific info!

flake.nix:

{
  inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; };

  outputs = inputs@{ flake-parts, ... }:
    flake-parts.lib.mkFlake { inherit inputs; } {
      systems =
        [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ];
      perSystem = { config, self', inputs', pkgs, system, ... }: {
        devShells.default = with pkgs;
          mkShell {
            name = "elixir_ls";

            # Define packages that will be available for our project
            buildInputs = [ elixir erlang elixir_ls ];

            ERL_AFLAGS = "-kernel shell_history enabled";

            # Define envvar $ELIXIR_SRC that points to Elixir sources, it looks like this:
            # /nix/store/cq9kb2qq08y95j2ym4by6f3jk0ix9sni-source
            ELIXIR_SRC = "${elixir.src}";
          };
      };
    };
}

This PR makes ElixirSense.Location to look up Elixir sources under the configured location as a fallback. This will allow for code navigation to Elixir sources to work on environments like NixOS.

Sadly, I have no idea how to write tests for this, so any suggestion is appreciated. Meanwhile I tested it manually on my machine in an IEX session:

iex(1)> ElixirSense.Location.find_mod_fun_source(String, :length, 1)
nil
iex(2)> ElixirSense.Location.find_type_source(DateTime, :t, 0)
nil
iex(3)> elixir_src = System.get_env("ELIXIR_SRC")
"/nix/store/cq9kb2qq08y95j2ym4by6f3jk0ix9sni-source"
iex(4)> Application.put_env(:elixir_sense, :elixir_src, elixir_src)
:ok
iex(5)> ElixirSense.Location.find_mod_fun_source(String, :length, 1)
%ElixirSense.Location{
  type: :function,
  file: "/nix/store/cq9kb2qq08y95j2ym4by6f3jk0ix9sni-source/lib/elixir/lib/string.ex",
  line: 2017,
  column: 3
}
iex(6)> ElixirSense.Location.find_type_source(DateTime, :t, 0)
%ElixirSense.Location{
  type: :typespec,
  file: "/nix/store/cq9kb2qq08y95j2ym4by6f3jk0ix9sni-source/lib/elixir/lib/calendar/datetime.ex",
  line: 125,
  column: 3
}

If this PR is merged, I will open another one in https://github.com/elixir-lsp/elixir-ls that will read $ELIXIR_SRC at the server startup and configure :elixir_sense accordingly.

@smaximov
Copy link
Contributor Author

I added two basic tests, but they will work only in environments that don't have Elixir sources. Please let me know what you think.

@lukaszsamson
Copy link
Collaborator

Nice, that was something on my todo list.

I added two basic tests, but they will work only in environments that don't have Elixir sources. Please let me know what you think.

Fake elixir source file is a right way here

Would you mind creating a similar PR adding OTP source location?

@lukaszsamson lukaszsamson merged commit 59e4c96 into elixir-lsp:master Aug 29, 2023
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants