elixir-tools / next-ls

The language server for Elixir that just works. Ready for early adopters!
https://www.elixir-tools.dev/next-ls
MIT License
691 stars 41 forks source link

Execute commands on emacs #471

Closed rockneurotiko closed 4 months ago

rockneurotiko commented 4 months ago

Hey!

I'm trying to implement the to-pipe and from-pipe in emacs, but the resulting edit is just wrong, and I'm not sure why.

I'm using the latest release (21.4)

Method to execute the command:

(defun rock--lsp-nextls-to-pipe ()
  "Execute to pipe on cursor."
  (interactive)
  (lsp-request
   "workspace/executeCommand"
   (list :command "to-pipe"
         :arguments (vector (list :uri (lsp--buffer-uri)
                                  :position
                                  (list :line
                                        (string-to-number (format-mode-line "%l"))
                                        :character
                                        (string-to-number (format-mode-line "%c"))))))
   :no-wait nil
   :no-merge nil))

Test module:

defmodule Test do
  def pipe(x) do
    Enum.map(x, & &1.id)
  end
end

I execute the command on the start of the Enum.map, and this is the result:

defmodule Test do
  pipe(x)
  |> def do
    Enum.map(x, & &1.id)
  end
end

This are the message sent and received:

[Trace - 01:03:55 PM] Sending request 'workspace/executeCommand - (228)'.
Params: {
  "command": "to-pipe",
  "arguments": [
    {
      "uri": "file:///home/<user>/<project>/lib/test.ex",
      "position": {
        "line": 3,
        "character": 4
      }
    }
  ]
}

[Trace - 01:03:55 PM] Received request 'workspace/applyEdit - (1539).
Params: {
  "edit": {
    "changes": {
      "file:///home/<user>/<project>/lib/test.ex": [
        {
          "newText": "pipe(x)\n  |> def do\n    Enum.map(x, & &1.id)\n  end",
          "range": {
            "end": {
              "character": 5,
              "line": 3
            },
            "start": {
              "character": 2,
              "line": 1
            }
          }
        }
      ]
    }
  },
  "label": "Extracted to a pipe"
}
mhanberg commented 4 months ago

You have to send the position with 0-indexed line and columns. This is decided by the LSP protocol.

So it should be line 2 and character 3.

rockneurotiko commented 4 months ago

@mhanberg

I just tried that and the result is the same:

[Trace - 01:17:05 PM] Sending request 'workspace/executeCommand - (255)'.
Params: {
  "command": "to-pipe",
  "arguments": [
    {
      "uri": "file:///home/<user>/<project>/lib/test.ex",
      "position": {
        "line": 2,
        "character": 3
      }
    }
  ]
}

[Trace - 01:17:05 PM] Received request 'workspace/applyEdit - (1795).
Params: {
  "edit": {
    "changes": {
      "file:///home/<user>/<project>/lib/test.ex": [
        {
          "newText": "pipe(x)\n  |> def do\n    Enum.map(x, & &1.id)\n  end",
          "range": {
            "end": {
              "character": 5,
              "line": 3
            },
            "start": {
              "character": 2,
              "line": 1
            }
          }
        }
      ]
    }
  },
  "label": "Extracted to a pipe"
}
NJichev commented 4 months ago

~The cursor position should also be on the argument you want extracted to a pipe, decision for this was that there are too many edge cases to cover otherwise. (In your case it should be character: 13 i think)~ I found an edge case when the cursor is on x and it doesn't work.

Orthogonally we might want to prevent seeing a do/end block and piping the function name into it @mhanberg?

mhanberg commented 4 months ago

Just recounted the characters, make sure that the character falls inside of the Enum expression.

So the lines are 2 and character is 4.

mhanberg commented 4 months ago

@NJichev Should still work with it being on the module, see video here https://www.elixir-tools.dev/docs/next-ls/commands/#to-pipe

rockneurotiko commented 4 months ago

Oh! I thought that just being at the start of the line would be enough.

I'm trying with different character positions and it works starting on this ones (using 0 indexing character)

En|um.map(x, & &1.id) (character 5)

My original issue is resolved, thanks for the fast answer!