Skip to content

Commit

Permalink
Merge pull request #12897 from koic/update_workspace_execute_command_…
Browse files Browse the repository at this point in the history
…lsp_method

Respect user's intentions with `workspace/executeCommand` LSP method
  • Loading branch information
koic committed May 16, 2024
2 parents 10cf5f0 + bbb177a commit 730794e
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#12897](https://github.com/rubocop/rubocop/pull/12897): Respect user's intentions with `workspace/executeCommand` LSP method. ([@koic][])
4 changes: 3 additions & 1 deletion docs/modules/ROOT/pages/configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,9 @@ Style/PerlBackrefs:
AutoCorrect: contextual
----

This setting prevents autocorrection during editing in the editor.
This setting prevents autocorrection during editing in the editor. e.g, with `textDocument/formatting` LSP method.
However `workspace/executeCommand` LSP method, which is triggered by intentional user actions, respects the user's intention for autocorrection.

Additionally, for cases like `Metrics` cops where the highlight range extends over the entire body of classes, modules, methods, or blocks
offending range will be confined to only name. This approach helps to avoid redundant and noisy offenses in editor display.

Expand Down
3 changes: 2 additions & 1 deletion docs/modules/ROOT/pages/usage/lsp.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ NOTE: `rubocop --lsp` is for starting LSP client, so users don't manually execut
RuboCop provides APIs for developers of original language server or tools analogous to LSP, using RuboCop as the backend, instead of the RuboCop's built-in LSP.

- `RuboCop::LSP.enable` enables LSP mode, customizing for LSP-specific features such as autocorrection and short offense message.
- `RuboCop::LSP.disable` disables LSP mode, which can be particularly useful for testing.
- `RuboCop::LSP.disable` disables LSP mode, which can be particularly useful for testing. And intentional autocorrection by the user can be specified.
e.g, `workspace/executeCommand` and `textDocument/codeAction` LSP methods.

When implementing custom cops, `RuboCop::LSP.enabled?` can be used to achieve behavior that considers these states.
11 changes: 9 additions & 2 deletions lib/rubocop/lsp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,15 @@ def enable
# Disable LSP.
#
# @return [void]
def disable
@enabled = false
def disable(&block)
if block
original = @enabled
@enabled = false
yield
@enabled = original
else
@enabled = false
end
end
end
end
8 changes: 7 additions & 1 deletion lib/rubocop/lsp/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,20 @@ def for(name)
end

uri = request[:params][:arguments][0][:uri]
formatted = nil

# The `workspace/executeCommand` is an LSP method triggered by intentional user actions,
# so the user's intention for autocorrection is respected.
LSP.disable { formatted = format_file(uri, command: command) }

@server.write(
id: request[:id],
method: 'workspace/applyEdit',
params: {
label: label,
edit: {
changes: {
uri => format_file(uri, command: command)
uri => formatted
}
}
}
Expand Down
49 changes: 49 additions & 0 deletions spec/rubocop/lsp/server_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,55 @@
end
end

describe 'execute command safe formatting with `Lint/UnusedBlockArgument` cop (`AutoCorrect: contextual`)' do
let(:requests) do
[
{
jsonrpc: '2.0',
method: 'textDocument/didOpen',
params: {
textDocument: {
languageId: 'ruby',
text: 'foo { |unused_variable| 42 }',
uri: 'file:///path/to/file.rb',
version: 0
}
}
}, {
jsonrpc: '2.0',
id: 99,
method: 'workspace/executeCommand',
params: {
command: 'rubocop.formatAutocorrects',
arguments: [uri: 'file:///path/to/file.rb']
}
}
]
end

it 'handles requests' do
expect(stderr).to eq('')
expect(messages.last).to eq(
jsonrpc: '2.0',
id: 99,
method: 'workspace/applyEdit',
params: {
label: 'Format with RuboCop autocorrects',
edit: {
changes: {
'file:///path/to/file.rb': [
newText: "foo { |_unused_variable| 42 }\n",
range: {
start: { line: 0, character: 0 }, end: { line: 1, character: 0 }
}
]
}
}
}
)
end
end

describe 'execute command unsafe formatting' do
let(:requests) do
[
Expand Down
32 changes: 32 additions & 0 deletions spec/rubocop/lsp_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,37 @@
expect(described_class.enabled?).to be(false)
end
end

context 'when `RuboCop::LSP.disable` with block is called after `RuboCop::LSP.enable`' do
before do
described_class.enable
described_class.disable { @inner = described_class.enabled? }
@outer = described_class.enabled?
end

it 'returns false within block' do
expect(@inner).to be(false) # rubocop:disable RSpec/InstanceVariable
end

it 'returns true without block' do
expect(@outer).to be(true) # rubocop:disable RSpec/InstanceVariable
end
end

context 'when `RuboCop::LSP.disable` with block is called after `RuboCop::LSP.disable`' do
before do
described_class.disable
described_class.disable { @inner = described_class.enabled? }
@outer = described_class.enabled?
end

it 'returns false within block' do
expect(@inner).to be(false) # rubocop:disable RSpec/InstanceVariable
end

it 'returns false without block' do
expect(@outer).to be(false) # rubocop:disable RSpec/InstanceVariable
end
end
end
end

0 comments on commit 730794e

Please sign in to comment.