elixir-editors / elixir-sublime-syntax

The most powerful Elixir for the most Sublime experience.
MIT License
45 stars 6 forks source link

Will not format- (Mix) Could not find an SCM for dependency :credo #56

Closed tfantina closed 1 year ago

tfantina commented 1 year ago

Sorry if this is a duplicate, or has been answered elsewhere.

I've turned on "mix_format" "on_save", it works in one project, but every other Elixir project I have I get this error in the console:

** (Mix) Could not find an SCM for dependency :credo from App.Umbrella.MixProject

This happens both when I try to save and when I manually run mix format from the command pallet. Running mix format in the terminal works without any issues, and generally speaking the project is up to date and working well (and can format in other code editors).

My user settings for the plugin are:

{
    "mix_format": {
        "on_save": true,
    },
}
princemaple commented 1 year ago

https://github.com/elixir-editors/elixir-sublime-syntax/issues/53#issuecomment-1350505296

azizk commented 1 year ago

I was looking into this and got distracted with something else. Funnily enough, I just coincidentally got the same error, but with :postgrex. What I did was to change the elixir and erlang version in .tool-versions. When you do that the first time, hex is missing and you get this error message. It coincides with an ElixirForum post where somebody had a similar issue.

Now the question probably is why the mix binary called in your configuration is missing the hex package manager (in all your other projects). Are you using something like asdf?

tfantina commented 1 year ago

Yes I'm using asdf.

@princemaple that is another can of worms. I've come to really like Sublime (since the death of Atom) I just can't seem to get anything Elixir working - not good since that's my fulltime gig.

azizk commented 1 year ago

I also work as an Elixir dev and Sublime Text has been working great so far. Try switching to NixOS – which I did recently – if you wanna start doubting your sanity... It's so damn hard until you've learned enough to know the ropes. :sweat_smile:

So asdf is probably the issue here. The thing is that Sublime will normally not use asdf at all unless you start it from your shell (at least on my old Manjaro system). When you start it using your Desktop it won't contain the ~/.asdf/shims path in the $PATH variable. Therefore it will use the mix binary installed by your OS, which may not have hex installed yet. So maybe that's the reason or part of it why one mix has hex installed but the others don't.

Let's try to debug this. Open your Sublime console and try these Python code snippets:

# Print $PATH
os.environ['PATH']
# Call `which mix`
import subprocess; subprocess.Popen(["which", "mix"], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
# Call `mix hex.info`
import subprocess; subprocess.Popen(["mix", "hex.info"], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
tfantina commented 1 year ago

Thanks @azizk I almost always start it from my shell subl ., but you are right which mix is showing: (b'/usr/local/bin/mix\n', b'') Running which mix from the terminal I get /Users/travis/.asdf/shims/mix which is correct. Then running mix hex.info pretty much the same error I've been seeing in the past:

(b'\n10:13:23.038 [error] beam/beam_load.c(551): Error loading function \'Elixir.Hex\':file_lstat/2: op put_tuple u x:\n  please re-compile this module with an Erlang/OTP 25 compiler\n\n\n\n10:13:23.051

Is this then just a matter of setting: /Users/travis/.asdf/shims/mix as a .env file in sublime-lsp or is there somewhere else I need to put this/something else I'd need to do.

tfantina commented 1 year ago

I added:

     "env": {
        "PATH": "/Users/travis/.asdf/shims/mix",
      },

To LSP-elixir.sublime-settings and it seems to have worked! LSP is running better than ever before. Thanks again @azizk

azizk commented 1 year ago

Hey, you're welcome!

For future reference: I assume your settings look something like this:

{
  "command": ["elixir-ls"],
  "enabled": true,
  "env": {
    "PATH": "/Users/travis/.asdf/shims/mix"
  }
}

I'm a bit curious but also confused how that solved the issue. So I guess you're using LSP's format command now, instead of Mix Format: File from ElixirSyntax? In any case, shouldn't the PATH env var point to the shims/ directory only?

tfantina commented 1 year ago

I think I closed this prematurely. @azizk that's essentially what I had. LSP.sublime-settings:

{
  "lsp_format_on_save": true
}

LSP-elixir.sublime-settings:

"env": {
    "PATH": "/Users/travis/.asdf/shims/mix",
},

While that got the LSP working it never actually got format on save working, I can run a command from the the pallet "LSP Format File" and that works but it dosen't format when I save.

So then I jump back into trying to get ElixirSyntax working:

ElixirSyntax.sublime-settings :

{
    "mix_format": {
        "env": {
            "PATH": "/Users/travis/.asdf/shims/", <- Based on your recommendation I changed this from `/shims/mix`
         },
        "cmd": ["mix", "format"],
        "on_save": true,
    },
}

That leads to this error:

$ cd /Users/travis/Documents/elixir-project && mix format /Users/travis/Documents/elixir-project/apps/admin_web/lib/admin_web/live/integrations_live/index.ex
# Timestamp: 2023-05-22 09:07:06

09:07:06.156 [error] beam/beam_load.c(551): Error loading function 'Elixir.Hex':file_lstat/2: op put_tuple u x:
  please re-compile this module with an Erlang/OTP 25 compiler

09:07:06.156 [error] Loading of /Users/travis/.mix/archives/hex-0.20.5/hex-0.20.5/ebin/Elixir.Hex.beam failed: :badfile

09:07:06.227 [error] beam/beam_load.c(551): Error loading function 'Elixir.Hex':file_lstat/2: op put_tuple u x:
  please re-compile this module with an Erlang/OTP 25 compiler

09:07:06.227 [error] Loading of /Users/travis/.mix/archives/hex-0.20.5/hex-0.20.5/ebin/Elixir.Hex.beam failed: :badfile

I think with this info the issue is pretty straightforward, it's pulling a super old version of hex that's been hanging around in an archives folder on my machine (which I will just delete), but I don't know why, like where is that being set? Especially since I can run mix format through the terminal and it works just fine.

princemaple commented 1 year ago

Do you see lsp-elixir at bottom left corner? (status bar)

tfantina commented 1 year ago

Yeah. In general LSP features are working, just not lsp_format_on_save. Whether that functionality comes from the LSP or ElixirFormatter I don't really care either way if I can get it working (I didn't realize how much I took auto format for granted all my builds are failing because of formatting issues 😆). Screen Shot 2023-05-23 at 8 23 18 AM

azizk commented 1 year ago

Not much time atm, but I'll try to help.

ElixirSyntax.sublime-settings :

The "env" setting doesn't exist yet for "mix_format". It's an easy feature to add but not necessary to solve this problem I think.

I think with this info the issue is pretty straightforward, it's pulling a super old version of hex that's been hanging around in an archives folder on my machine (which I will just delete), but I don't know why, like where is that being set? Especially since I can run mix format through the terminal and it works just fine.

Well, the path ~/.mix/archives indicates that it's not using asdf and you have an ancient version of hex installed. You could remedy that problem by deleting the folder and running something like /usr/bin/mix local.hex --force (avoid asdf!). As for why it works fine in your terminal: I think that's because asdf provides the mix executable and you have a recent version of hex somewhere in ~/.asdf.

princemaple commented 1 year ago

I'll try and dig from the LSP side.

  1. Do you see these logs when you first open an elixir file in SublimeText console?
LSP: starting ['C:\\Users\\...\\AppData\\Local\\Sublime Text\\Package Storage\\LSP-elixir\\server\\0.14.6\\language_server.bat'] in C:\Users\...\code\...
LSP: lsp-elixir: Supported execute commands: ['expandMacro:7YC7-EJBoSaRKM0lGPPCu7UBbXey2u0A', 'getExUnitTestsInFile:7YC7-EJBoSaRKM0lGPPCu7UBbXey2u0A', 'manipulatePipes:7YC7-EJBoSaRKM0lGPPCu7UBbXey2u0A', 'mixClean:7YC7-EJBoSaRKM0lGPPCu7UBbXey2u0A', 'restart:7YC7-EJBoSaRKM0lGPPCu7UBbXey2u0A', 'spec:7YC7-EJBoSaRKM0lGPPCu7UBbXey2u0A']
LSP: lsp-elixir: registering capability: didChangeWatchedFilesProvider

  1. After adding these LSP settings:
  "log_max_size": 0,
  "log_debug": true,
  "log_server": [
    "panel"
  ],

If you run this command LSP: Toggle Log Panel, change a file, save. Do you see these logs in the panel?

:: [08:29:31.804] --> lsp-elixir textDocument/formatting (22): {'workDoneToken': '$ublime-work-done-progress-22', 'options': {'trimTrailingWhitespace': True, 'trimFinalNewlines': True, 'tabSize': 2, 'insertSpaces': True, 'insertFinalNewline': True}, 'textDocument': {'uri': 'file:///C:/Users/.../code/.../lib/app/release.ex'}}
:: [08:29:31.810] <<< lsp-elixir (22) (duration: 6ms): [{'range': {'start': {'character': 4, 'line': 25}, 'end': {'character': 4, 'line': 26}}, 'newText': ''}]
:: [08:29:31.833]  -> lsp-elixir textDocument/didChange: {'contentChanges': [{'range': {'start': {'character': 4, 'line': 25}, 'end': {'character': 4, 'line': 26}}, 'rangeLength': 5, 'text': ''}], 'textDocument': {'version': 18, 'uri': 'file:///C:/Users/.../code/.../lib/app/release.ex'}}
:: [08:29:31.833]  -> lsp-elixir textDocument/didSave: {'text': '...code...', 'textDocument': {'uri': 'file:///C:/Users/.../code/.../lib/app/release.ex'}}
lsp-elixir: Starting build with MIX_ENV: dev MIX_TARGET: host
tfantina commented 1 year ago

Thanks for helping out with this @princemaple and @azizk.

@princemaple when I look at the logs starting I see a bunch of plugin reloads but nothing like what you are indicating. These seems to be the most related lines:

reloading python 3.3 plugin LSP-elixir.plugin
reloading python 3.3 plugin LSP-elixir.server_zip_resource
reloading python 3.3 plugin LSP-elixir.update
Traceback (most recent call last):
  File "/Applications/Sublime Text.app/Contents/MacOS/Lib/python33/sublime_plugin.py", line 308, in reload_plugin
    m = importlib.import_module(modulename)
  File "./python3.3/importlib/__init__.py", line 90, in import_module
  File "<frozen importlib._bootstrap>", line 1584, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1565, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1532, in _find_and_load_unlocked
  File "/Applications/Sublime Text.app/Contents/MacOS/Lib/python33/sublime_plugin.py", line 1692, in load_module
    exec(compile(source, source_path, 'exec'), mod.__dict__)
  File "/Users/travis/Library/Application Support/Sublime Text/Installed Packages/LSP-elixir.sublime-package/update.py", line 18
    print(f"Current version: {current_version}")
                                              ^
SyntaxError: invalid syntax

After adding the debug logs I see this in the panel when I save:

 [19:56:05.397]  -> lsp-elixir textDocument/didChange: {'textDocument': {'uri': 'file:///Users/travis/Documents/project/apps/admin_web/lib/admin_web/controllers/password_controller.ex', 'version': 40}, 'contentChanges': [{'rangeLength': 1, 'range': {'end': {'character': 0, 'line': 7}, 'start': {'character': 27, 'line': 6}}, 'text': ''}]}
:: [19:56:05.399] --> lsp-elixir textDocument/codeLens (23): {'textDocument': {'uri': 'file:///Users/travis/Documents/project/apps/admin_web/lib/admin_web/controllers/password_controller.ex'}}
:: [19:56:05.420] <<< lsp-elixir (23) (duration: 21ms): []
:: [19:56:09.415]  -> lsp-elixir textDocument/didSave: {'textDocument': {'uri': 'file:///Users/travis/Documents/project/apps/admin_web/lib/admin_web/controllers/password_controller.ex'}, 'text': 'defmodule AdminWeb.PasswordController do\n  alias PacksizeNow.Accounts\n  alias PacksizeNow.Accounts.{ChangePasswordInput, ForgotPasswordInput}\n\n  import PacksizeNow.Kernel\n\n  use AdminWeb, :controller\n  plug :manual_authorization\n\n  #\n  # Initiate Password Reset Actions\n  #\n\n  @spec new(Plug.Conn.t(), map()) :: Plug.Conn.t()\n  def new(conn, _), do: render(conn, "forgot.html", changeset: ForgotPasswordInput.changeset(%{}))\n\n  @spec create(Plug.Conn.t(), map()) :: Plug.Conn.t()\n  def create(conn, %{"forgot_password_input" => params}) do\n    case Accounts.forgot_password_with_input(params, send_notification: &send_reset_email/2) do\n      {:ok, %{email: email}} ->\n        conn\n        |> put_flash(:success, "Further instructions have been sent to #{email}.")\n        |> redirect(to: Routes.password_path(conn, :new))\n\n      {:error, changeset} ->\n        render(conn, "forgot.html", changeset: changeset)\n    end\n  end\n\n  defp hello() do \n    "wow"\n  end \n\n  defp send_reset_email(%{} = reset, opts) do\n    email = Notifications.UserEmail.reset_admin_password(reset, opts)\n    Notifications.Mailer.deliver_now(email, opts)\n\n    ok(nil)\n  end\n\n  #\n  # Complete Password Reset Actions\n  #\n\n  plug :load_reset_or_redirect when action in [:edit, :update]\n\n  @spec edit(Plug.Conn.t(), map()) :: Plug.Conn.t()\n  def edit(conn, %{"user_id" => user_id, "token" => token}) do\n    render(conn, "reset.html",\n      changeset: ChangePasswordInput.changeset(%{}),\n      user_id: user_id,\n      token: token\n    )\n  end\n\n  @spec update(Plug.Conn.t(), map()) :: Plug.Conn.t()\n  def update(conn, %{"user_id" => user_id, "token" => token, "change_password_input" => params}) do\n    case Accounts.reset_password(user_id, token, params) do\n      {:ok, _data} ->\n        conn\n        |> put_flash(:success, "Your password has been updated, you may now sign in.")\n        |> redirect(to: Routes.session_path(conn, :new))\n\n      {:error, changeset} ->\n        conn\n        |> put_flash(:error, "Please ensure all fields are filled out correctly to continue.")\n        |> assign(:user_id, user_id)\n        |> assign(:token, token)\n        |> assign(:changeset, changeset)\n        |> render("reset.html")\n    end\n  end\n\n  defp load_reset_or_redirect(%{params: %{"user_id" => user_id, "token" => token}} = conn, _opts) do\n    case Accounts.load_password_reset(user_id, token) do\n      %{} = reset_request ->\n        assign(conn, :reset_request, reset_request)\n\n      _ ->\n        conn\n        |> put_flash(:error, "That password reset request has expired or already been used.")\n        |> redirect(to: Routes.password_path(conn, :new))\n        |> halt()\n    end\n  end\nend\n'}
lsp-elixir: Starting build with MIX_ENV: test MIX_TARGET: host
:: [19:56:09.421] <-  lsp-elixir window/logMessage: {'type': 3, 'message': 'Starting build with MIX_ENV: test MIX_TARGET: host'}
lsp-elixir: warning: String.strip/1 is deprecated. Use String.trim/1 instead
  /Users/travis/Documents/project/deps/poison/mix.exs:4: Poison.Mixfile

:: [19:56:12.421] <-  lsp-elixir window/logMessage: {'type': 2, 'message': 'warning: String.strip/1 is deprecated. Use String.trim/1 instead\n  /Users/travis/Documents/project/deps/poison/mix.exs:4: Poison.Mixfile\n'}
lsp-elixir: All deps are up to date
:: [19:56:14.856] <-  lsp-elixir window/logMessage: {'type': 4, 'message': 'All deps are up to date'}
princemaple commented 1 year ago
    print(f"Current version: {current_version}")
                                              ^

That is a known issue but probably isn't the real cause. Do you see a crash dump at your project root? If you are on OTP 26, it's expected to crash elixir-ls compiled from low OTP version (lsp-elixir by default uses a lower version for best compatibility). I've just released a new patch version to mitigate the python issue. I see you have successful communication logs with the server, so I guess you are not on OTP 26, and the server indeed wasn't simply killed by the python issue.

As you are not seeing the textDocument/formatting line that I see in my logs, I suspect the problem is still in the settings. Could you try and set this in your elixir syntax specific setting? Simply open an elixir file, then go to Preferences -> Settings - Syntax Specific, add the following then restart Sublime.

{
  "lsp_format_on_save": true
}
tfantina commented 1 year ago

I'm on OTP 25. Still no luck with that. I can see textDocument/formatting if I open command pallet and run LSP Format but it's not happening on save.

princemaple commented 1 year ago

That's rather odd. I'm out of ideas. You might want to ask in LSP channel on sublimehq Discord.

princemaple commented 1 year ago

I asked on Discord. Got a couple good answers.

Do you have vintage or neovintageous installed? How do you save the file?

LSP has this command lsp_save. Auto format only works when lsp_save is triggered. If you could open sublime console and run sublime.log_commands(True). And try your way of saving the file. You are supposed to see lsp_save in the commands, otherwise it wouldn't work. So, if you don't see it, you probably just need to figure out a way to trigger that command when you save.

LSP comes with this key binding:

    {
        "keys": ["primary+s"],
        "command": "lsp_save",
        "context": [{"key": "lsp.session_with_capability", "operand": "textDocumentSync.willSave | textDocumentSync.willSaveWaitUntil | codeActionProvider.codeActionKinds | documentFormattingProvider | documentRangeFormattingProvider"}]
    },

If you aren't using primary+s (ctrl+s on windows/linux, command+s on mac), you won't trigger it. You need to fix that :)

tfantina commented 1 year ago

Oh snap! It's that keybinding. My key map didn't have it, I don't know why. Thank you so much, you are a god among humans.

princemaple commented 1 year ago

😄