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

Make workspace/willRenameFiles working #2531

Open
wants to merge 70 commits into
base: master
Choose a base branch
from

Conversation

przepompownia
Copy link
Contributor

@przepompownia przepompownia commented Feb 4, 2024

Fixes #2522
Fixes #2523

@przepompownia
Copy link
Contributor Author

przepompownia commented Feb 4, 2024

@dantleech at the moment it seems to work on simple cases but

  • we need to verify if we need the part described in workspace/willRenameFiles - destination file must exist before #2522 (currently commented out)
  • I'm aware that it's badly designed at the moment - I mean using WorkspaceEdit directly in FileRenamer. Probably we need to define some equivalent of Phpactor\Rename\Model\LocatedTextEditsMap instead and convert it to WorkspaceEdit in the handler.

@przepompownia
Copy link
Contributor Author

To test it with Neovim (master) I use

local function willRenameFilesHandler(response)
  for clientId, clientResponse in pairs(response) do
    local client = vim.lsp.get_client_by_id(clientId)
    if nil == client then
      vim.notify(('Client %s does not exist'):format(clientId), vim.log.levels.ERROR, {title = 'LSP: workspace/willRenameFiles'})

      return
    end

    vim.lsp.util.apply_workspace_edit(clientResponse.result, client.offset_encoding)
    -- vim.print(clientResponse.result)
  end
end

local function willRenameCurrentBuffer(newPath)
  if not vim.startswith(newPath, '/') then
    newPath = vim.fs.joinpath(vim.uv.cwd(), newPath)
  end
  vim.lsp.buf_request_all(
    0,
    'workspace/willRenameFiles', 
    {
      files = { { oldUri = vim.uri_from_bufnr(0), newUri = vim.uri_from_fname(newPath) } },
    },
    willRenameFilesHandler
  )
end
vim.api.nvim_create_user_command('WillRenameCurrentBuffer', function (opts)
  willRenameCurrentBuffer(opts.args)
end, {nargs = 1, complete = 'file'})

@przepompownia przepompownia marked this pull request as ready for review February 25, 2024 03:49
@przepompownia
Copy link
Contributor Author

przepompownia commented Feb 25, 2024

@dantleech at the moment it seems to work on simple cases but

* we need to verify if we need the part described in [`workspace/willRenameFiles` - destination file must exist before #2522](https://github.com/phpactor/phpactor/issues/2522) (currently commented out)

* I'm aware that it's badly designed at the moment - I mean using `WorkspaceEdit` directly in `FileRenamer`. Probably we need to define some equivalent of `Phpactor\Rename\Model\LocatedTextEditsMap` instead and convert it to `WorkspaceEdit` in the handler.

At the moment both above things are done:

  • replaceDefinition() is needed to edit the source file itself before moving it (in the meaning of WorkspaceEdit changes)
  • to avoid reinvention of models and use RenameResult in handler I changed both the adapter and handler into a pattern used in ClassRenamer.

@przepompownia
Copy link
Contributor Author

Composing the summarized WorkspaceEdit instance from component instances can be controversial. It assumes (relying on LocatedTextEditConverter::toWorkspaceEdit implementation) that only documentChanges field is not empty in each of them and we can ignore other fields.

https://github.com/phpactor/phpactor/pull/2531/files#diff-39ca8889abb439b22964fc403e2e98d48192ef428529ede2bee62f84221faedfR60-R71

*/
class RenameEdit extends ArrayIterator
{
public function __construct(LocatedTextEditsMap|RenameResult|null ...$edits)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this a variadic?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only for enforcing types without using docblock.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let's use the docblock then :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem, regardless, I'd like to know why you think this is probably bad practice (especially that in practice the constructor gets max two arguments).

Copy link
Collaborator

@dantleech dantleech Mar 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

variadics are fine but i'm confused: why is it an array at all and not LocatedTextEdits $edits, RenameResult $result ?

Copy link
Collaborator

@dantleech dantleech Apr 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still confused. The result of the Promise in lib/Rename/Adapter/ClassMover/FileRenamer.php is:

            return new WorkspaceRenameEdits([
                LocatedTextEditsMap::fromLocatedEdits($locatedEdits),
                new RenameResult($from, $to),
            ]);

i.e. why not:

            return new WorkspaceRenameEdits(
                LocatedTextEditsMap::fromLocatedEdits($locatedEdits),
                new RenameResult($from, $to),
            );

It doesn't need to be an array?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't need to be an array?

It's (similarly to WorkspaceEdit's documentChanges elements) the only way to keep the order of operations here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify though, it's not needed now?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, we are also passing an aggregate of located text edits, rather than the individual edits. So it's a bit ... weird. If this is gojg to be a list of operations then we should or bably not use the LocatedTextEditsMap here, as that' not an atomic operation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this separation is far from the best and even weird. I would like to leave some design assumptions to you, but I realize that this may mean a lot of reconstruction (and, in effect, wait probably long time). Please feel free to replace it with something better. I'm not attached in any way the current form of solution. It would be good to have working such LSP request.

@przepompownia
Copy link
Contributor Author

przepompownia commented Mar 30, 2024

I'm confused about why phpstan is complaining about changes unrelated to this PR after merge #2614

Edit: most of them were probably secondary to the few unsilenced.

$previous = $error->getPrevious();

$this->clientApi->window()->showMessage()->error(sprintf(
$error->getMessage() . ($previous?->getTraceAsString() ?? '')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is sprintf missing a format string? %s %s why do we get the trace from $previous?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied catching CouldNotRename from RenameHandler to have the same way of error handling and don't know what the trace was needed for in the original place:

https://github.com/phpactor/phpactor/blame/a23130918b61c3f45f4a403abe56b5a06be80de9/lib/Extension/LanguageServerRename/Handler/RenameHandler.php#L93

- Rename WorkspaceRenameEdits => WorkspaceOperations
- Removes dead variable
- Use `merge` to accumulate changes to the operations
- Only convert once
- Use IteratorAggregate instead of extending ArrayIterator
...i.e. LocatedTextDocuemntEdits maps to the LSP TextDocumentEdits
@dantleech
Copy link
Collaborator

I've made a PR on your PR: przepompownia#1 would be great if you can verify that it's all working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants