rucoa 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/Gemfile.lock +1 -1
- data/README.md +4 -2
- data/lib/rucoa/cli.rb +2 -2
- data/lib/rucoa/configuration.rb +63 -0
- data/lib/rucoa/diagnostic_provider.rb +12 -28
- data/lib/rucoa/formatting_provider.rb +2 -0
- data/lib/rucoa/handler_concerns/configuration_requestable.rb +35 -0
- data/lib/rucoa/handler_concerns/diagnostics_publishable.rb +32 -0
- data/lib/rucoa/handler_concerns.rb +8 -0
- data/lib/rucoa/handlers/base.rb +61 -0
- data/lib/rucoa/handlers/initialize_handler.rb +22 -0
- data/lib/rucoa/handlers/initialized_handler.rb +13 -0
- data/lib/rucoa/handlers/text_document_code_action_handler.rb +29 -0
- data/lib/rucoa/handlers/text_document_did_change_handler.rb +31 -0
- data/lib/rucoa/handlers/text_document_did_open_handler.rb +31 -0
- data/lib/rucoa/handlers/text_document_formatting_handler.rb +33 -0
- data/lib/rucoa/handlers/text_document_range_formatting_handler.rb +24 -0
- data/lib/rucoa/handlers/text_document_selection_range_handler.rb +25 -0
- data/lib/rucoa/handlers/workspace_did_change_configuration_handler.rb +13 -0
- data/lib/rucoa/handlers.rb +16 -0
- data/lib/rucoa/message_reader.rb +3 -1
- data/lib/rucoa/message_writer.rb +3 -0
- data/lib/rucoa/range.rb +35 -9
- data/lib/rucoa/range_formatting_provider.rb +67 -0
- data/lib/rucoa/rubocop_configuration_checker.rb +42 -0
- data/lib/rucoa/selection_range_provider.rb +3 -1
- data/lib/rucoa/server.rb +79 -124
- data/lib/rucoa/source_store.rb +7 -1
- data/lib/rucoa/version.rb +1 -1
- data/lib/rucoa.rb +5 -0
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7206c3901e7ceea282b2de34f31ab90cc5ffa7fa48c06e58ae64bb718f48cd52
|
4
|
+
data.tar.gz: 6188a2047317b98160565599de3dc08663e732d7b54a546a8bc3e610379cb39a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afa9c37dae4a5c460eb1f5505ca511138c20f5b8c483a5ba68f798d1f9b18a5f5c4a481ee3ec743eb8585328538b5daec613b9f69a9599e5337106c57b1c0ea8
|
7
|
+
data.tar.gz: 539517254cff781a99b6ec463aed13bd04cbbcf3da3a9ff15cf72dac809b12c53ef9ff53c51828a163975bbdbe60a174767a472d2b4476f03f1813b5f513c62d
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -35,11 +35,13 @@ gem install rucoa
|
|
35
35
|
- Diagnostics
|
36
36
|
- Formatting
|
37
37
|
- Quick Fix
|
38
|
-
- Selection
|
38
|
+
- Selection Range
|
39
39
|
|
40
40
|
### Coming soon
|
41
41
|
|
42
42
|
- Completion
|
43
43
|
- Documentation
|
44
|
-
- Highlight
|
45
44
|
- Go to Definition
|
45
|
+
- Highlight
|
46
|
+
- Semantic Tokens
|
47
|
+
- Symbol
|
data/lib/rucoa/cli.rb
CHANGED
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rucoa
|
4
|
+
class Configuration
|
5
|
+
DEFAULT_SETTINGS = {
|
6
|
+
'base' => {
|
7
|
+
'enable' => 'auto',
|
8
|
+
'useBundler' => 'auto'
|
9
|
+
},
|
10
|
+
'feature' => {
|
11
|
+
'codeAction' => {
|
12
|
+
'enable' => true
|
13
|
+
},
|
14
|
+
'diagnostics' => {
|
15
|
+
'enable' => true
|
16
|
+
},
|
17
|
+
'formatting' => {
|
18
|
+
'enable' => true
|
19
|
+
},
|
20
|
+
'selectionRange' => {
|
21
|
+
'enable' => true
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
reset
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Boolean]
|
31
|
+
def enables_code_action?
|
32
|
+
@settings.dig('feature', 'codeAction', 'enable')
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Boolean]
|
36
|
+
def enables_diagnostics?
|
37
|
+
@settings.dig('feature', 'diagnostics', 'enable')
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Boolean]
|
41
|
+
def enables_formatting?
|
42
|
+
@settings.dig('feature', 'formatting', 'enable')
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Boolean]
|
46
|
+
def enables_selection_range?
|
47
|
+
@settings.dig('feature', 'selectionRange', 'enable')
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param settings [Hash]
|
51
|
+
# @return [void]
|
52
|
+
def update(settings)
|
53
|
+
@settings = settings
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# @return [void]
|
59
|
+
def reset
|
60
|
+
@settings = DEFAULT_SETTINGS.dup
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'pathname'
|
4
|
-
|
5
3
|
module Rucoa
|
6
4
|
class DiagnosticProvider
|
7
5
|
# @param source [Rucoa::Source]
|
@@ -23,11 +21,12 @@ module Rucoa
|
|
23
21
|
|
24
22
|
# @return [Array<Hash>]
|
25
23
|
def call
|
26
|
-
return [] unless
|
24
|
+
return [] unless RubocopConfigurationChecker.call
|
27
25
|
|
28
26
|
offenses.map do |offense|
|
29
27
|
OffenseToDiagnosticMapper.call(
|
30
28
|
offense,
|
29
|
+
source: @source,
|
31
30
|
uri: @uri
|
32
31
|
)
|
33
32
|
end
|
@@ -40,26 +39,6 @@ module Rucoa
|
|
40
39
|
RubocopInvestigator.call(source: @source)
|
41
40
|
end
|
42
41
|
|
43
|
-
# @return [Boolean]
|
44
|
-
def rubocop_configured?
|
45
|
-
each_ancestor_pathname.any? do |pathname|
|
46
|
-
pathname.join('.rubocop.yml').exist?
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
# @return [Enumerable<Pathname>]
|
51
|
-
def each_ancestor_pathname
|
52
|
-
return to_enum(__method__) unless block_given?
|
53
|
-
|
54
|
-
pathname = ::Pathname.new(@source.path)
|
55
|
-
loop do
|
56
|
-
pathname = pathname.parent
|
57
|
-
yield pathname
|
58
|
-
break if pathname.root?
|
59
|
-
end
|
60
|
-
self
|
61
|
-
end
|
62
|
-
|
63
42
|
class OffenseToDiagnosticMapper
|
64
43
|
LSP_SEVERITY_NAME_TO_VALUE_MAP = {
|
65
44
|
error: 1,
|
@@ -81,17 +60,24 @@ module Rucoa
|
|
81
60
|
|
82
61
|
class << self
|
83
62
|
# @param offense [RuboCop::Cop::Offense]
|
63
|
+
# @param source [Rucoa::Source]
|
84
64
|
# @param uri [String]
|
85
65
|
# @return [Hash]
|
86
|
-
def call(offense, uri:)
|
87
|
-
new(
|
66
|
+
def call(offense, source:, uri:)
|
67
|
+
new(
|
68
|
+
offense,
|
69
|
+
source: source,
|
70
|
+
uri: uri
|
71
|
+
).call
|
88
72
|
end
|
89
73
|
end
|
90
74
|
|
91
75
|
# @param offense [RuboCop::Cop::Offense]
|
76
|
+
# @param source [Rucoa::Source]
|
92
77
|
# @param uri [String]
|
93
|
-
def initialize(offense, uri:)
|
78
|
+
def initialize(offense, source:, uri:)
|
94
79
|
@offense = offense
|
80
|
+
@source = source
|
95
81
|
@uri = uri
|
96
82
|
end
|
97
83
|
|
@@ -119,8 +105,6 @@ module Rucoa
|
|
119
105
|
{
|
120
106
|
cop_name: @offense.cop_name,
|
121
107
|
edits: edits,
|
122
|
-
path: @offense.location.source_buffer.name,
|
123
|
-
range: range,
|
124
108
|
uri: @uri
|
125
109
|
}
|
126
110
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rucoa
|
4
|
+
module HandlerConcerns
|
5
|
+
module ConfigurationRequestable
|
6
|
+
include HandlerConcerns::DiagnosticsPublishable
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
# @return [void]
|
11
|
+
def request_workspace_configuration
|
12
|
+
write(
|
13
|
+
method: 'workspace/configuration',
|
14
|
+
params: {
|
15
|
+
items: [
|
16
|
+
{
|
17
|
+
section: 'rucoa'
|
18
|
+
}
|
19
|
+
]
|
20
|
+
}
|
21
|
+
) do |response|
|
22
|
+
configuration.update(response['result'][0])
|
23
|
+
publish_diagnostics_on_each_source
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [void]
|
28
|
+
def publish_diagnostics_on_each_source
|
29
|
+
source_store.each_uri do |uri|
|
30
|
+
publish_diagnostics_on(uri)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rucoa
|
4
|
+
module HandlerConcerns
|
5
|
+
module DiagnosticsPublishable
|
6
|
+
private
|
7
|
+
|
8
|
+
# @param uri [String]
|
9
|
+
# @return [Array<Hash>]
|
10
|
+
def diagnostics_on(uri)
|
11
|
+
return [] unless configuration.enables_diagnostics?
|
12
|
+
|
13
|
+
DiagnosticProvider.call(
|
14
|
+
source: source_store.get(uri),
|
15
|
+
uri: uri
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param uri [String]
|
20
|
+
# @return [void]
|
21
|
+
def publish_diagnostics_on(uri)
|
22
|
+
write(
|
23
|
+
method: 'textDocument/publishDiagnostics',
|
24
|
+
params: {
|
25
|
+
diagnostics: diagnostics_on(uri),
|
26
|
+
uri: uri
|
27
|
+
}
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rucoa
|
4
|
+
module Handlers
|
5
|
+
class Base
|
6
|
+
class << self
|
7
|
+
# @param server [Rucoa::Server]
|
8
|
+
# @param request [Hash]
|
9
|
+
# @return [void]
|
10
|
+
def call(request:, server:)
|
11
|
+
new(
|
12
|
+
request: request,
|
13
|
+
server: server
|
14
|
+
).call
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param request [Hash]
|
19
|
+
# @param server [Rucoa::Server]
|
20
|
+
def initialize(request:, server:)
|
21
|
+
@request = request
|
22
|
+
@server = server
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [void]
|
26
|
+
def call
|
27
|
+
raise ::NotImplementedError
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# @return [Hash]
|
33
|
+
attr_reader :request
|
34
|
+
|
35
|
+
# @param message [Hash]
|
36
|
+
# @return [void]
|
37
|
+
def respond(message)
|
38
|
+
write(
|
39
|
+
id: request['id'],
|
40
|
+
result: message
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Rucoa::Configuration]
|
45
|
+
def configuration
|
46
|
+
@server.configuration
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Rucoa::SourceStore]
|
50
|
+
def source_store
|
51
|
+
@server.source_store
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param message [Hash]
|
55
|
+
# @return [void]
|
56
|
+
def write(message, &block)
|
57
|
+
@server.write(message, &block)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rucoa
|
4
|
+
module Handlers
|
5
|
+
class InitializeHandler < Base
|
6
|
+
def call
|
7
|
+
respond(
|
8
|
+
capabilities: {
|
9
|
+
codeActionProvider: true,
|
10
|
+
documentFormattingProvider: true,
|
11
|
+
documentRangeFormattingProvider: true,
|
12
|
+
selectionRangeProvider: true,
|
13
|
+
textDocumentSync: {
|
14
|
+
change: 1, # Full
|
15
|
+
openClose: true
|
16
|
+
}
|
17
|
+
}
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rucoa
|
4
|
+
module Handlers
|
5
|
+
class TextDocumentCodeActionHandler < Base
|
6
|
+
def call
|
7
|
+
return unless diagnostics
|
8
|
+
|
9
|
+
respond(code_actions)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
# @return [Array<Hash>]
|
15
|
+
def code_actions
|
16
|
+
return [] unless configuration.enables_code_action?
|
17
|
+
|
18
|
+
CodeActionProvider.call(
|
19
|
+
diagnostics: diagnostics
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Array<Hash>, nil]
|
24
|
+
def diagnostics
|
25
|
+
@diagnostics ||= request.dig('params', 'context', 'diagnostics')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rucoa
|
4
|
+
module Handlers
|
5
|
+
class TextDocumentDidChangeHandler < Base
|
6
|
+
include HandlerConcerns::DiagnosticsPublishable
|
7
|
+
|
8
|
+
def call
|
9
|
+
update_source
|
10
|
+
publish_diagnostics_on(uri)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# @return [void]
|
16
|
+
def update_source
|
17
|
+
source_store.set(uri, text)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String]
|
21
|
+
def text
|
22
|
+
request.dig('params', 'contentChanges')[0]['text']
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String]
|
26
|
+
def uri
|
27
|
+
@uri ||= request.dig('params', 'textDocument', 'uri')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rucoa
|
4
|
+
module Handlers
|
5
|
+
class TextDocumentDidOpenHandler < Base
|
6
|
+
include HandlerConcerns::DiagnosticsPublishable
|
7
|
+
|
8
|
+
def call
|
9
|
+
update_source
|
10
|
+
publish_diagnostics_on(uri)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# @return [void]
|
16
|
+
def update_source
|
17
|
+
source_store.set(uri, text)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String]
|
21
|
+
def text
|
22
|
+
request.dig('params', 'textDocument', 'text')
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String]
|
26
|
+
def uri
|
27
|
+
@uri ||= request.dig('params', 'textDocument', 'uri')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rucoa
|
4
|
+
module Handlers
|
5
|
+
class TextDocumentFormattingHandler < Base
|
6
|
+
def call
|
7
|
+
respond(edits)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
# @return [Array<Hash>]
|
13
|
+
def edits
|
14
|
+
return unless configuration.enables_formatting?
|
15
|
+
return unless source
|
16
|
+
|
17
|
+
FormattingProvider.call(
|
18
|
+
source: source
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Rucoa::Source, nil]
|
23
|
+
def source
|
24
|
+
@source ||= source_store.get(uri)
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [String]
|
28
|
+
def uri
|
29
|
+
request.dig('params', 'textDocument', 'uri')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rucoa
|
4
|
+
module Handlers
|
5
|
+
class TextDocumentRangeFormattingHandler < Base
|
6
|
+
def call
|
7
|
+
return unless configuration.enables_formatting?
|
8
|
+
|
9
|
+
uri = request.dig('params', 'textDocument', 'uri')
|
10
|
+
source = source_store.get(uri)
|
11
|
+
return unless source
|
12
|
+
|
13
|
+
respond(
|
14
|
+
RangeFormattingProvider.call(
|
15
|
+
range: Range.from_vscode_range(
|
16
|
+
request.dig('params', 'range')
|
17
|
+
),
|
18
|
+
source: source
|
19
|
+
)
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rucoa
|
4
|
+
module Handlers
|
5
|
+
class TextDocumentSelectionRangeHandler < Base
|
6
|
+
def call
|
7
|
+
return unless configuration.enables_selection_range?
|
8
|
+
|
9
|
+
source = source_store.get(
|
10
|
+
request.dig('params', 'textDocument', 'uri')
|
11
|
+
)
|
12
|
+
return unless source
|
13
|
+
|
14
|
+
respond(
|
15
|
+
request.dig('params', 'positions').filter_map do |position|
|
16
|
+
SelectionRangeProvider.call(
|
17
|
+
position: Position.from_vscode_position(position),
|
18
|
+
source: source
|
19
|
+
)
|
20
|
+
end
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rucoa
|
4
|
+
module Handlers
|
5
|
+
autoload :Base, 'rucoa/handlers/base'
|
6
|
+
autoload :InitializeHandler, 'rucoa/handlers/initialize_handler'
|
7
|
+
autoload :InitializedHandler, 'rucoa/handlers/initialized_handler'
|
8
|
+
autoload :TextDocumentCodeActionHandler, 'rucoa/handlers/text_document_code_action_handler'
|
9
|
+
autoload :TextDocumentDidChangeHandler, 'rucoa/handlers/text_document_did_change_handler'
|
10
|
+
autoload :TextDocumentDidOpenHandler, 'rucoa/handlers/text_document_did_open_handler'
|
11
|
+
autoload :TextDocumentFormattingHandler, 'rucoa/handlers/text_document_formatting_handler'
|
12
|
+
autoload :TextDocumentRangeFormattingHandler, 'rucoa/handlers/text_document_range_formatting_handler'
|
13
|
+
autoload :TextDocumentSelectionRangeHandler, 'rucoa/handlers/text_document_selection_range_handler'
|
14
|
+
autoload :WorkspaceDidChangeConfigurationHandler, 'rucoa/handlers/workspace_did_change_configuration_handler'
|
15
|
+
end
|
16
|
+
end
|
data/lib/rucoa/message_reader.rb
CHANGED
@@ -11,8 +11,10 @@ module Rucoa
|
|
11
11
|
end
|
12
12
|
|
13
13
|
# @yieldparam message [Hash]
|
14
|
-
# @return [void]
|
14
|
+
# @return [Enumerator<Hash>, void]
|
15
15
|
def read
|
16
|
+
return enum_for(:read) unless block_given?
|
17
|
+
|
16
18
|
while (buffer = @io.gets("\r\n\r\n"))
|
17
19
|
content_length = buffer[/Content-Length: (\d+)/i, 1]
|
18
20
|
raise Errors::ContentLengthHeaderNotFound unless content_length
|
data/lib/rucoa/message_writer.rb
CHANGED
data/lib/rucoa/range.rb
CHANGED
@@ -11,19 +11,49 @@ module Rucoa
|
|
11
11
|
Position.from_parser_range_ending(range)
|
12
12
|
)
|
13
13
|
end
|
14
|
+
|
15
|
+
# @param hash [Hash]
|
16
|
+
# @return [Rucoa::Range]
|
17
|
+
def from_vscode_range(hash)
|
18
|
+
new(
|
19
|
+
Position.from_vscode_position(hash['start']),
|
20
|
+
Position.from_vscode_position(hash['end'])
|
21
|
+
)
|
22
|
+
end
|
14
23
|
end
|
15
24
|
|
25
|
+
# @return [Rucoa::Position]
|
26
|
+
attr_reader :beginning
|
27
|
+
|
28
|
+
# @return [Rucoa::Position]
|
29
|
+
attr_reader :ending
|
30
|
+
|
16
31
|
# @param beginning [Rucoa::Position]
|
17
32
|
# @param ending [Ruoca::Position]
|
18
|
-
|
33
|
+
# @param exclude_end [Boolean]
|
34
|
+
def initialize(beginning, ending, exclude_end: true)
|
19
35
|
@beginning = beginning
|
20
36
|
@ending = ending
|
37
|
+
@exclude_end = exclude_end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param range [Rucoa::Range]
|
41
|
+
# @return [Boolean]
|
42
|
+
def contains?(range)
|
43
|
+
copy = with_including_end
|
44
|
+
copy.include?(range.beginning) && copy.include?(range.ending)
|
21
45
|
end
|
22
46
|
|
23
47
|
# @param position [Rucoa::Position]
|
24
48
|
# @return [Boolean]
|
25
49
|
def include?(position)
|
26
|
-
|
50
|
+
return false if position.line > @ending.line
|
51
|
+
return false if position.line < @beginning.line
|
52
|
+
return false if position.column < @beginning.column
|
53
|
+
return false if position.column > @ending.column
|
54
|
+
return false if position.column == @ending.column && @exclude_end
|
55
|
+
|
56
|
+
true
|
27
57
|
end
|
28
58
|
|
29
59
|
# @return [Hash]
|
@@ -36,13 +66,9 @@ module Rucoa
|
|
36
66
|
|
37
67
|
private
|
38
68
|
|
39
|
-
# @
|
40
|
-
|
41
|
-
|
42
|
-
position.line > @ending.line ||
|
43
|
-
position.line < @beginning.line ||
|
44
|
-
(position.line == @beginning.line && position.column < @beginning.column) ||
|
45
|
-
(position.line == @ending.line && position.column >= @ending.column)
|
69
|
+
# @return [Rucoa::Range]
|
70
|
+
def with_including_end
|
71
|
+
self.class.new(@beginning, @ending, exclude_end: false)
|
46
72
|
end
|
47
73
|
end
|
48
74
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rucoa
|
4
|
+
class RangeFormattingProvider
|
5
|
+
class << self
|
6
|
+
# @param range [Rucoa::Range]
|
7
|
+
# @param source [Rucoa::Source]
|
8
|
+
# @return [Array<Hash>]
|
9
|
+
def call(range:, source:)
|
10
|
+
new(range: range, source: source).call
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param range [Rucoa::Range]
|
15
|
+
# @param source [Rucoa::Source]
|
16
|
+
def initialize(range:, source:)
|
17
|
+
@range = range
|
18
|
+
@source = source
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Array<Hash>]
|
22
|
+
def call
|
23
|
+
return [] unless RubocopConfigurationChecker.call
|
24
|
+
|
25
|
+
edits
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# @return [Array<RuboCop::Cop::Corrector>]
|
31
|
+
def correctable_offenses
|
32
|
+
offenses.select(&:corrector)
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Array(Rucoa::Range, String)]
|
36
|
+
def correctable_replacements
|
37
|
+
replacements.select do |range, _|
|
38
|
+
@range.contains?(range)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Array<Hash>]
|
43
|
+
def edits
|
44
|
+
correctable_replacements.map do |range, replacement|
|
45
|
+
{
|
46
|
+
newText: replacement,
|
47
|
+
range: range.to_vscode_range
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Array<RuboCop::Cop::Offense>]
|
53
|
+
def offenses
|
54
|
+
RubocopInvestigator.call(source: @source)
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Array(Rucoa::Range, String)]
|
58
|
+
def replacements
|
59
|
+
correctable_offenses.map(&:corrector).flat_map(&:as_replacements).map do |range, replacement|
|
60
|
+
[
|
61
|
+
Range.from_parser_range(range),
|
62
|
+
replacement
|
63
|
+
]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module Rucoa
|
6
|
+
class RubocopConfigurationChecker
|
7
|
+
class << self
|
8
|
+
# @return [Boolean]
|
9
|
+
def call
|
10
|
+
new.call
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Boolean]
|
15
|
+
def call
|
16
|
+
rubocop_configured_for_current_directory?
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# @return [Boolean]
|
22
|
+
def rubocop_configured_for_current_directory?
|
23
|
+
each_current_and_ancestor_pathname.any? do |pathname|
|
24
|
+
pathname.join('.rubocop.yml').exist?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Enumerable<Pathname>]
|
29
|
+
def each_current_and_ancestor_pathname
|
30
|
+
return to_enum(__method__) unless block_given?
|
31
|
+
|
32
|
+
pathname = ::Pathname.pwd
|
33
|
+
loop do
|
34
|
+
yield pathname
|
35
|
+
break if pathname.root?
|
36
|
+
|
37
|
+
pathname = pathname.parent
|
38
|
+
end
|
39
|
+
self
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/rucoa/server.rb
CHANGED
@@ -2,158 +2,113 @@
|
|
2
2
|
|
3
3
|
module Rucoa
|
4
4
|
class Server
|
5
|
-
# @
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
# @return [Hash{String => Class}]
|
6
|
+
METHOD_TO_HANDLER_MAP = {
|
7
|
+
'initialize' => Handlers::InitializeHandler,
|
8
|
+
'initialized' => Handlers::InitializedHandler,
|
9
|
+
'textDocument/codeAction' => Handlers::TextDocumentCodeActionHandler,
|
10
|
+
'textDocument/didChange' => Handlers::TextDocumentDidChangeHandler,
|
11
|
+
'textDocument/didOpen' => Handlers::TextDocumentDidOpenHandler,
|
12
|
+
'textDocument/formatting' => Handlers::TextDocumentFormattingHandler,
|
13
|
+
'textDocument/rangeFormatting' => Handlers::TextDocumentRangeFormattingHandler,
|
14
|
+
'textDocument/selectionRange' => Handlers::TextDocumentSelectionRangeHandler,
|
15
|
+
'workspace/didChangeConfiguration' => Handlers::WorkspaceDidChangeConfigurationHandler
|
16
|
+
}.freeze
|
17
|
+
private_constant :METHOD_TO_HANDLER_MAP
|
18
|
+
|
19
|
+
# @return [Rucoa::Configuration]
|
20
|
+
attr_reader :configuration
|
21
|
+
|
22
|
+
# @return [Rucoa::SourceStore]
|
23
|
+
attr_reader :source_store
|
24
|
+
|
25
|
+
# @param input [IO]
|
26
|
+
# @param output [IO]
|
27
|
+
def initialize(input:, output:)
|
28
|
+
@reader = MessageReader.new(input)
|
29
|
+
@writer = MessageWriter.new(output)
|
30
|
+
|
31
|
+
@configuration = Configuration.new
|
32
|
+
@server_request_id = 0
|
33
|
+
@client_response_handlers = {}
|
10
34
|
@source_store = SourceStore.new
|
11
35
|
end
|
12
36
|
|
13
37
|
# @return [void]
|
14
38
|
def start
|
15
|
-
read do |request|
|
16
|
-
|
17
|
-
if result
|
18
|
-
write(
|
19
|
-
request: request,
|
20
|
-
result: result
|
21
|
-
)
|
22
|
-
end
|
39
|
+
@reader.read do |request|
|
40
|
+
handle(request)
|
23
41
|
end
|
24
42
|
end
|
25
43
|
|
26
|
-
|
27
|
-
|
28
|
-
# @
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
when 'textDocument/codeAction'
|
35
|
-
on_text_document_code_action(request)
|
36
|
-
when 'textDocument/didChange'
|
37
|
-
on_text_document_did_change(request)
|
38
|
-
when 'textDocument/didOpen'
|
39
|
-
on_text_document_did_open(request)
|
40
|
-
when 'textDocument/formatting'
|
41
|
-
on_text_document_formatting(request)
|
42
|
-
when 'textDocument/selectionRange'
|
43
|
-
on_text_document_selection_range(request)
|
44
|
+
# @yieldparam response [Hash]
|
45
|
+
# @param message [Hash]
|
46
|
+
# @return [void]
|
47
|
+
def write(message, &block)
|
48
|
+
if block
|
49
|
+
write_server_request(message, &block)
|
50
|
+
else
|
51
|
+
write_server_response(message)
|
44
52
|
end
|
45
53
|
end
|
46
54
|
|
47
|
-
# @
|
48
|
-
# @return [
|
49
|
-
def
|
50
|
-
@
|
55
|
+
# @note This method is for testing.
|
56
|
+
# @return [Array<Hash>]
|
57
|
+
def responses
|
58
|
+
io = @writer.io
|
59
|
+
io.rewind
|
60
|
+
MessageReader.new(io).read.to_a
|
51
61
|
end
|
52
62
|
|
53
|
-
|
54
|
-
# @param result [Object]
|
55
|
-
# @return [void]
|
56
|
-
def write(request:, result:)
|
57
|
-
@writer.write(
|
58
|
-
{
|
59
|
-
id: request['id'],
|
60
|
-
result: result
|
61
|
-
}
|
62
|
-
)
|
63
|
-
end
|
63
|
+
private
|
64
64
|
|
65
|
-
# @param
|
65
|
+
# @param request [Hash]
|
66
66
|
# @return [void]
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
uri: uri
|
74
|
-
),
|
75
|
-
uri: uri
|
76
|
-
}
|
77
|
-
)
|
78
|
-
end
|
79
|
-
|
80
|
-
# @param _request [Hash]
|
81
|
-
# @return [Hash]
|
82
|
-
def on_initialize(_request)
|
83
|
-
{
|
84
|
-
capabilities: {
|
85
|
-
codeActionProvider: true,
|
86
|
-
documentFormattingProvider: true,
|
87
|
-
selectionRangeProvider: true,
|
88
|
-
textDocumentSync: {
|
89
|
-
change: 1, # Full
|
90
|
-
openClose: true
|
91
|
-
}
|
92
|
-
}
|
93
|
-
}
|
67
|
+
def handle(request)
|
68
|
+
if request['method']
|
69
|
+
handle_client_request(request)
|
70
|
+
elsif request['id']
|
71
|
+
handle_client_response(request)
|
72
|
+
end
|
94
73
|
end
|
95
74
|
|
96
75
|
# @param request [Hash]
|
97
|
-
# @return [
|
98
|
-
def
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
CodeActionProvider.call(
|
103
|
-
diagnostics: diagnostics
|
76
|
+
# @return [void]
|
77
|
+
def handle_client_request(request)
|
78
|
+
find_client_request_handler(request['method'])&.call(
|
79
|
+
request: request,
|
80
|
+
server: self
|
104
81
|
)
|
105
82
|
end
|
106
83
|
|
107
|
-
# @param
|
108
|
-
# @return [
|
109
|
-
def
|
110
|
-
|
111
|
-
@source_store.set(
|
112
|
-
uri,
|
113
|
-
request.dig('params', 'contentChanges')[0]['text']
|
114
|
-
)
|
115
|
-
investigate_diagnostics(uri: uri)
|
116
|
-
nil
|
84
|
+
# @param response [Hash]
|
85
|
+
# @return [void]
|
86
|
+
def handle_client_response(response)
|
87
|
+
@client_response_handlers.delete(response['id'])&.call(response)
|
117
88
|
end
|
118
89
|
|
119
|
-
# @param
|
120
|
-
# @return [nil]
|
121
|
-
def
|
122
|
-
|
123
|
-
@source_store.set(
|
124
|
-
uri,
|
125
|
-
request.dig('params', 'textDocument', 'text')
|
126
|
-
)
|
127
|
-
investigate_diagnostics(uri: uri)
|
128
|
-
nil
|
90
|
+
# @param request_method [String]
|
91
|
+
# @return [Class, nil]
|
92
|
+
def find_client_request_handler(request_method)
|
93
|
+
METHOD_TO_HANDLER_MAP[request_method]
|
129
94
|
end
|
130
95
|
|
131
|
-
# @param
|
132
|
-
# @return [
|
133
|
-
def
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
FormattingProvider.call(
|
139
|
-
source: source
|
96
|
+
# @param message [Hash]
|
97
|
+
# @return [void]
|
98
|
+
def write_server_request(message, &block)
|
99
|
+
@writer.write(
|
100
|
+
message.merge(
|
101
|
+
id: @server_request_id
|
102
|
+
)
|
140
103
|
)
|
104
|
+
@client_response_handlers[@server_request_id] = block
|
105
|
+
@server_request_id += 1
|
141
106
|
end
|
142
107
|
|
143
|
-
# @param
|
144
|
-
# @return [
|
145
|
-
def
|
146
|
-
|
147
|
-
request.dig('params', 'textDocument', 'uri')
|
148
|
-
)
|
149
|
-
return unless source
|
150
|
-
|
151
|
-
request.dig('params', 'positions').filter_map do |position|
|
152
|
-
SelectionRangeProvider.call(
|
153
|
-
position: Position.from_vscode_position(position),
|
154
|
-
source: source
|
155
|
-
)
|
156
|
-
end
|
108
|
+
# @param message [Hash]
|
109
|
+
# @return [void]
|
110
|
+
def write_server_response(message)
|
111
|
+
@writer.write(message)
|
157
112
|
end
|
158
113
|
end
|
159
114
|
end
|
data/lib/rucoa/source_store.rb
CHANGED
@@ -25,13 +25,19 @@ module Rucoa
|
|
25
25
|
)
|
26
26
|
end
|
27
27
|
|
28
|
+
# @yieldparam uri [String]
|
29
|
+
# @return [Enumerable<String>]
|
30
|
+
def each_uri(&block)
|
31
|
+
@data.each_key(&block)
|
32
|
+
end
|
33
|
+
|
28
34
|
private
|
29
35
|
|
30
36
|
# @param uri [String]
|
31
37
|
# @return [String]
|
32
38
|
def path_from_uri(uri)
|
33
39
|
::CGI.unescape(
|
34
|
-
::URI.parse(uri).path
|
40
|
+
::URI.parse(uri).path || 'untitled'
|
35
41
|
)
|
36
42
|
end
|
37
43
|
end
|
data/lib/rucoa/version.rb
CHANGED
data/lib/rucoa.rb
CHANGED
@@ -5,9 +5,12 @@ require_relative 'rucoa/version'
|
|
5
5
|
module Rucoa
|
6
6
|
autoload :Cli, 'rucoa/cli'
|
7
7
|
autoload :CodeActionProvider, 'rucoa/code_action_provider'
|
8
|
+
autoload :Configuration, 'rucoa/configuration'
|
8
9
|
autoload :DiagnosticProvider, 'rucoa/diagnostic_provider'
|
9
10
|
autoload :Errors, 'rucoa/errors'
|
10
11
|
autoload :FormattingProvider, 'rucoa/formatting_provider'
|
12
|
+
autoload :HandlerConcerns, 'rucoa/handler_concerns'
|
13
|
+
autoload :Handlers, 'rucoa/handlers'
|
11
14
|
autoload :MessageReader, 'rucoa/message_reader'
|
12
15
|
autoload :MessageWriter, 'rucoa/message_writer'
|
13
16
|
autoload :Nodes, 'rucoa/nodes'
|
@@ -15,7 +18,9 @@ module Rucoa
|
|
15
18
|
autoload :ParserBuilder, 'rucoa/parser_builder'
|
16
19
|
autoload :Position, 'rucoa/position'
|
17
20
|
autoload :Range, 'rucoa/range'
|
21
|
+
autoload :RangeFormattingProvider, 'rucoa/range_formatting_provider'
|
18
22
|
autoload :RubocopAutocorrector, 'rucoa/rubocop_autocorrector'
|
23
|
+
autoload :RubocopConfigurationChecker, 'rucoa/rubocop_configuration_checker'
|
19
24
|
autoload :RubocopInvestigator, 'rucoa/rubocop_investigator'
|
20
25
|
autoload :SelectionRangeProvider, 'rucoa/selection_range_provider'
|
21
26
|
autoload :Server, 'rucoa/server'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rucoa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryo Nakamura
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-09-
|
11
|
+
date: 2022-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parser
|
@@ -58,9 +58,24 @@ files:
|
|
58
58
|
- lib/rucoa.rb
|
59
59
|
- lib/rucoa/cli.rb
|
60
60
|
- lib/rucoa/code_action_provider.rb
|
61
|
+
- lib/rucoa/configuration.rb
|
61
62
|
- lib/rucoa/diagnostic_provider.rb
|
62
63
|
- lib/rucoa/errors.rb
|
63
64
|
- lib/rucoa/formatting_provider.rb
|
65
|
+
- lib/rucoa/handler_concerns.rb
|
66
|
+
- lib/rucoa/handler_concerns/configuration_requestable.rb
|
67
|
+
- lib/rucoa/handler_concerns/diagnostics_publishable.rb
|
68
|
+
- lib/rucoa/handlers.rb
|
69
|
+
- lib/rucoa/handlers/base.rb
|
70
|
+
- lib/rucoa/handlers/initialize_handler.rb
|
71
|
+
- lib/rucoa/handlers/initialized_handler.rb
|
72
|
+
- lib/rucoa/handlers/text_document_code_action_handler.rb
|
73
|
+
- lib/rucoa/handlers/text_document_did_change_handler.rb
|
74
|
+
- lib/rucoa/handlers/text_document_did_open_handler.rb
|
75
|
+
- lib/rucoa/handlers/text_document_formatting_handler.rb
|
76
|
+
- lib/rucoa/handlers/text_document_range_formatting_handler.rb
|
77
|
+
- lib/rucoa/handlers/text_document_selection_range_handler.rb
|
78
|
+
- lib/rucoa/handlers/workspace_did_change_configuration_handler.rb
|
64
79
|
- lib/rucoa/message_reader.rb
|
65
80
|
- lib/rucoa/message_writer.rb
|
66
81
|
- lib/rucoa/nodes.rb
|
@@ -70,7 +85,9 @@ files:
|
|
70
85
|
- lib/rucoa/parser_builder.rb
|
71
86
|
- lib/rucoa/position.rb
|
72
87
|
- lib/rucoa/range.rb
|
88
|
+
- lib/rucoa/range_formatting_provider.rb
|
73
89
|
- lib/rucoa/rubocop_autocorrector.rb
|
90
|
+
- lib/rucoa/rubocop_configuration_checker.rb
|
74
91
|
- lib/rucoa/rubocop_investigator.rb
|
75
92
|
- lib/rucoa/selection_range_provider.rb
|
76
93
|
- lib/rucoa/server.rb
|