rucoa 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -0
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +4 -1
  5. data/README.md +7 -4
  6. data/lib/rucoa/cli.rb +2 -2
  7. data/lib/rucoa/configuration.rb +71 -0
  8. data/lib/rucoa/handler_concerns/configuration_requestable.rb +35 -0
  9. data/lib/rucoa/handler_concerns/diagnostics_publishable.rb +172 -0
  10. data/lib/rucoa/handler_concerns.rb +8 -0
  11. data/lib/rucoa/handlers/base.rb +61 -0
  12. data/lib/rucoa/handlers/initialize_handler.rb +23 -0
  13. data/lib/rucoa/handlers/initialized_handler.rb +13 -0
  14. data/lib/rucoa/handlers/text_document_code_action_handler.rb +104 -0
  15. data/lib/rucoa/handlers/text_document_did_change_handler.rb +31 -0
  16. data/lib/rucoa/handlers/text_document_did_open_handler.rb +31 -0
  17. data/lib/rucoa/handlers/text_document_document_symbol_handler.rb +241 -0
  18. data/lib/rucoa/handlers/text_document_formatting_handler.rb +64 -0
  19. data/lib/rucoa/handlers/text_document_range_formatting_handler.rb +76 -0
  20. data/lib/rucoa/handlers/text_document_selection_range_handler.rb +141 -0
  21. data/lib/rucoa/handlers/workspace_did_change_configuration_handler.rb +13 -0
  22. data/lib/rucoa/handlers.rb +17 -0
  23. data/lib/rucoa/message_reader.rb +3 -1
  24. data/lib/rucoa/message_writer.rb +3 -0
  25. data/lib/rucoa/node_concerns/name_full_qualifiable.rb +20 -0
  26. data/lib/rucoa/node_concerns.rb +7 -0
  27. data/lib/rucoa/nodes/base.rb +13 -0
  28. data/lib/rucoa/nodes/casgn_node.rb +14 -0
  29. data/lib/rucoa/nodes/class_node.rb +8 -0
  30. data/lib/rucoa/nodes/const_node.rb +12 -0
  31. data/lib/rucoa/nodes/def_node.rb +12 -0
  32. data/lib/rucoa/nodes/defs_node.rb +12 -0
  33. data/lib/rucoa/nodes/module_node.rb +21 -0
  34. data/lib/rucoa/nodes/sclass_node.rb +8 -0
  35. data/lib/rucoa/nodes/send_node.rb +17 -0
  36. data/lib/rucoa/nodes/str_node.rb +4 -0
  37. data/lib/rucoa/nodes/sym_node.rb +12 -0
  38. data/lib/rucoa/nodes.rb +9 -0
  39. data/lib/rucoa/parser_builder.rb +19 -10
  40. data/lib/rucoa/range.rb +32 -15
  41. data/lib/rucoa/rubocop_autocorrector.rb +38 -0
  42. data/lib/rucoa/rubocop_configuration_checker.rb +42 -0
  43. data/lib/rucoa/{rubocop_runner.rb → rubocop_investigator.rb} +9 -8
  44. data/lib/rucoa/server.rb +80 -97
  45. data/lib/rucoa/source.rb +2 -2
  46. data/lib/rucoa/source_store.rb +7 -1
  47. data/lib/rucoa/version.rb +1 -1
  48. data/lib/rucoa.rb +8 -4
  49. metadata +32 -5
  50. data/lib/rucoa/diagnostic_provider.rb +0 -112
  51. data/lib/rucoa/selection_range_provider.rb +0 -97
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ceb1c198d4cc4deef7d9b122f4d50ca77c4c9e57713b96069278d7b827f9ba05
4
- data.tar.gz: 7d826934f96b44bfbde2f0b75670b4d6678c665c6651f5ece3b463150dbd985f
3
+ metadata.gz: 1656b3603d72a44829a30fd9bd0d0d876ab9daa3af05cad16ad78298fa6fce7d
4
+ data.tar.gz: ff2fffdb7e90ed0f23cf782bc5c25db77dabb327c3e5b1d0a92aad8d2a5dc201
5
5
  SHA512:
6
- metadata.gz: 76179fc8d424e84b810ed46135a9a39f403e4799dece8743bf44c48adf478b0467c0774c6827312868c5e4f41baa98b161448e3442e24f9c04b5836d0b390982
7
- data.tar.gz: 9ffa4f84e54f7592cdb669934778b1a2d0e23cc6b3ea356a189e62daf935bb54dc69f10095bddd8a93f797ec7b5e308cecf9923d78154c2c1634798b344cd512
6
+ metadata.gz: 49421faba6f35b3cf0bd33c12ae521cf55125fc8c8b044f5a4d353b4ee9413150b0ca4082884e786329b1cefa3cb5eb5ab513b9723e638cb9786ab768d2f8dd0
7
+ data.tar.gz: 8ef7dfaf46a49d70eb9404558191aadb3e1778f761a4642c432b53c9e3b1f208ff57f64a5b43ec7aef527dc4b573baaed10f4d859043574e86452b0af60aecb4
data/.rubocop.yml CHANGED
@@ -2,6 +2,7 @@ require:
2
2
  - rubocop-performance
3
3
  - rubocop-rake
4
4
  - rubocop-rspec
5
+ - sevencop
5
6
 
6
7
  AllCops:
7
8
  NewCops: enable
@@ -19,12 +20,18 @@ RSpec/ExampleLength:
19
20
  RSpec/ImplicitSubject:
20
21
  Enabled: false
21
22
 
23
+ RSpec/MultipleExpectations:
24
+ Enabled: false
25
+
22
26
  RSpec/MultipleMemoizedHelpers:
23
27
  Enabled: false
24
28
 
25
29
  RSpec/NamedSubject:
26
30
  Enabled: false
27
31
 
32
+ Sevencop/HashLiteralOrder:
33
+ Enabled: true
34
+
28
35
  Style/Documentation:
29
36
  Enabled: false
30
37
 
data/Gemfile CHANGED
@@ -10,3 +10,4 @@ gem 'rubocop'
10
10
  gem 'rubocop-performance'
11
11
  gem 'rubocop-rake'
12
12
  gem 'rubocop-rspec'
13
+ gem 'sevencop'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rucoa (0.1.0)
4
+ rucoa (0.4.0)
5
5
  parser
6
6
  rubocop
7
7
 
@@ -51,6 +51,8 @@ GEM
51
51
  rubocop-rspec (2.12.1)
52
52
  rubocop (~> 1.31)
53
53
  ruby-progressbar (1.11.0)
54
+ sevencop (0.9.3)
55
+ rubocop
54
56
  unicode-display_width (2.2.0)
55
57
 
56
58
  PLATFORMS
@@ -64,6 +66,7 @@ DEPENDENCIES
64
66
  rubocop-rake
65
67
  rubocop-rspec
66
68
  rucoa!
69
+ sevencop
67
70
 
68
71
  BUNDLED WITH
69
72
  2.3.21
data/README.md CHANGED
@@ -6,7 +6,7 @@ Language server for Ruby.
6
6
 
7
7
  ## Usage
8
8
 
9
- Install both rucoa gem and [vscode-rucoa](https://github.com/r7kamura/vscode-rucoa) extension, then open your Ruby project in VSCode.
9
+ Install both rucoa gem and [vscode-rucoa](https://marketplace.visualstudio.com/items?itemName=r7kamura.vscode-rucoa) extension, then open your Ruby project in VSCode.
10
10
 
11
11
  To install rucoa gem, if your project is managed by bundler, add to your Gemfile:
12
12
 
@@ -33,12 +33,15 @@ gem install rucoa
33
33
  ## Features
34
34
 
35
35
  - Diagnostics
36
- - Selection Ranges (experimental)
36
+ - Formatting
37
+ - Quick Fix
38
+ - Selection Range
39
+ - Symbol
37
40
 
38
41
  ### Coming soon
39
42
 
40
43
  - Completion
41
44
  - Documentation
42
- - Formatting
43
- - Highlight
44
45
  - Go to Definition
46
+ - Highlight
47
+ - Semantic Tokens
data/lib/rucoa/cli.rb CHANGED
@@ -16,8 +16,8 @@ module Rucoa
16
16
  # @return [void]
17
17
  def call
18
18
  Server.new(
19
- reader: $stdin,
20
- writer: $stdout
19
+ input: $stdin,
20
+ output: $stdout
21
21
  ).start
22
22
  end
23
23
  end
@@ -0,0 +1,71 @@
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
+ 'documentSymbol' => {
18
+ 'enable' => true
19
+ },
20
+ 'formatting' => {
21
+ 'enable' => true
22
+ },
23
+ 'selectionRange' => {
24
+ 'enable' => true
25
+ }
26
+ }
27
+ }.freeze
28
+
29
+ def initialize
30
+ reset
31
+ end
32
+
33
+ # @return [Boolean]
34
+ def enables_code_action?
35
+ @settings.dig('feature', 'codeAction', 'enable')
36
+ end
37
+
38
+ # @return [Boolean]
39
+ def enables_diagnostics?
40
+ @settings.dig('feature', 'diagnostics', 'enable')
41
+ end
42
+
43
+ # @return [Boolean]
44
+ def enables_document_symbol?
45
+ @settings.dig('feature', 'documentSymbol', 'enable')
46
+ end
47
+
48
+ # @return [Boolean]
49
+ def enables_formatting?
50
+ @settings.dig('feature', 'formatting', 'enable')
51
+ end
52
+
53
+ # @return [Boolean]
54
+ def enables_selection_range?
55
+ @settings.dig('feature', 'selectionRange', 'enable')
56
+ end
57
+
58
+ # @param settings [Hash]
59
+ # @return [void]
60
+ def update(settings)
61
+ @settings = settings
62
+ end
63
+
64
+ private
65
+
66
+ # @return [void]
67
+ def reset
68
+ @settings = DEFAULT_SETTINGS.dup
69
+ end
70
+ end
71
+ 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,172 @@
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
+
31
+ class DiagnosticProvider
32
+ # @param source [Rucoa::Source]
33
+ # @param uri [String]
34
+ # @return [Array<Hash>]
35
+ def self.call(source:, uri:)
36
+ new(
37
+ source: source,
38
+ uri: uri
39
+ ).call
40
+ end
41
+
42
+ # @param source [Rucoa::Source]
43
+ # @param uri [String]
44
+ def initialize(source:, uri:)
45
+ @source = source
46
+ @uri = uri
47
+ end
48
+
49
+ # @return [Array<Hash>]
50
+ def call
51
+ return [] unless RubocopConfigurationChecker.call
52
+
53
+ offenses.map do |offense|
54
+ OffenseToDiagnosticMapper.call(
55
+ offense,
56
+ source: @source,
57
+ uri: @uri
58
+ )
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ # @return [Array<RuboCop::Cop::Offense>]
65
+ def offenses
66
+ RubocopInvestigator.call(source: @source)
67
+ end
68
+
69
+ class OffenseToDiagnosticMapper
70
+ LSP_SEVERITY_NAME_TO_VALUE_MAP = {
71
+ error: 1,
72
+ hint: 4,
73
+ information: 3,
74
+ warning: 2
75
+ }.freeze
76
+ private_constant :LSP_SEVERITY_NAME_TO_VALUE_MAP
77
+
78
+ RUBOCOP_SEVERITY_NAME_TO_LSP_SEVERITY_MAP = {
79
+ convention: LSP_SEVERITY_NAME_TO_VALUE_MAP[:information],
80
+ error: LSP_SEVERITY_NAME_TO_VALUE_MAP[:error],
81
+ fatal: LSP_SEVERITY_NAME_TO_VALUE_MAP[:error],
82
+ info: LSP_SEVERITY_NAME_TO_VALUE_MAP[:information],
83
+ refactor: LSP_SEVERITY_NAME_TO_VALUE_MAP[:hint],
84
+ warning: LSP_SEVERITY_NAME_TO_VALUE_MAP[:warning]
85
+ }.freeze
86
+ private_constant :RUBOCOP_SEVERITY_NAME_TO_LSP_SEVERITY_MAP
87
+
88
+ class << self
89
+ # @param offense [RuboCop::Cop::Offense]
90
+ # @param source [Rucoa::Source]
91
+ # @param uri [String]
92
+ # @return [Hash]
93
+ def call(offense, source:, uri:)
94
+ new(
95
+ offense,
96
+ source: source,
97
+ uri: uri
98
+ ).call
99
+ end
100
+ end
101
+
102
+ # @param offense [RuboCop::Cop::Offense]
103
+ # @param source [Rucoa::Source]
104
+ # @param uri [String]
105
+ def initialize(offense, source:, uri:)
106
+ @offense = offense
107
+ @source = source
108
+ @uri = uri
109
+ end
110
+
111
+ # @return [Hash]
112
+ def call
113
+ {
114
+ code: code,
115
+ data: data,
116
+ message: message,
117
+ range: range,
118
+ severity: severity,
119
+ source: source
120
+ }
121
+ end
122
+
123
+ private
124
+
125
+ # @return [String]
126
+ def code
127
+ @offense.cop_name
128
+ end
129
+
130
+ # @return [Hash]
131
+ def data
132
+ {
133
+ cop_name: @offense.cop_name,
134
+ edits: edits,
135
+ uri: @uri
136
+ }
137
+ end
138
+
139
+ # @return [Array<Hash>, nil]
140
+ def edits
141
+ @offense.corrector&.as_replacements&.map do |range, replacement|
142
+ {
143
+ newText: replacement,
144
+ range: Range.from_parser_range(range).to_vscode_range
145
+ }
146
+ end
147
+ end
148
+
149
+ # @return [String]
150
+ def message
151
+ @offense.message.delete_prefix("#{@offense.cop_name}: ")
152
+ end
153
+
154
+ # @return [Hash]
155
+ def range
156
+ Range.from_parser_range(@offense.location).to_vscode_range
157
+ end
158
+
159
+ # @return [Integer]
160
+ def severity
161
+ RUBOCOP_SEVERITY_NAME_TO_LSP_SEVERITY_MAP.fetch(@offense.severity.name, 1)
162
+ end
163
+
164
+ # @return [String]
165
+ def source
166
+ 'RuboCop'
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module HandlerConcerns
5
+ autoload :ConfigurationRequestable, 'rucoa/handler_concerns/configuration_requestable'
6
+ autoload :DiagnosticsPublishable, 'rucoa/handler_concerns/diagnostics_publishable'
7
+ end
8
+ 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,23 @@
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
+ documentSymbolProvider: true,
13
+ selectionRangeProvider: true,
14
+ textDocumentSync: {
15
+ change: 1, # Full
16
+ openClose: true
17
+ }
18
+ }
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Handlers
5
+ class InitializedHandler < Base
6
+ include HandlerConcerns::ConfigurationRequestable
7
+
8
+ def call
9
+ request_workspace_configuration
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,104 @@
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
+ correctable_diagnostics.map do |diagnostic|
19
+ DiagnosticToCodeActionMapper.call(diagnostic)
20
+ end
21
+ end
22
+
23
+ # @return [Array<Hash>]
24
+ def correctable_diagnostics
25
+ diagnostics.select do |diagnostic|
26
+ diagnostic.dig('data', 'edits')
27
+ end
28
+ end
29
+
30
+ # @return [Array<Hash>, nil]
31
+ def diagnostics
32
+ @diagnostics ||= request.dig('params', 'context', 'diagnostics')
33
+ end
34
+
35
+ class DiagnosticToCodeActionMapper
36
+ class << self
37
+ # @param diagnostic [Hash]
38
+ # @return [Hash]
39
+ def call(diagnostic)
40
+ new(diagnostic).call
41
+ end
42
+ end
43
+
44
+ # @param diagnostic [Hash]
45
+ def initialize(diagnostic)
46
+ @diagnostic = diagnostic
47
+ end
48
+
49
+ # @return [Hash]
50
+ def call
51
+ {
52
+ diagnostics: diagnostics,
53
+ edit: edit,
54
+ isPreferred: preferred?,
55
+ kind: kind,
56
+ title: title
57
+ }
58
+ end
59
+
60
+ private
61
+
62
+ # @return [Hash]
63
+ def edit
64
+ {
65
+ documentChanges: [
66
+ {
67
+ edits: @diagnostic.dig('data', 'edits'),
68
+ textDocument: {
69
+ uri: @diagnostic.dig('data', 'uri'),
70
+ version: nil
71
+ }
72
+ }
73
+ ]
74
+ }
75
+ end
76
+
77
+ # @return [String]
78
+ def cop_name
79
+ @diagnostic.dig('data', 'cop_name')
80
+ end
81
+
82
+ # @return [Array]
83
+ def diagnostics
84
+ [@diagnostic]
85
+ end
86
+
87
+ # @return [Boolean]
88
+ def preferred?
89
+ true
90
+ end
91
+
92
+ # @return [String]
93
+ def kind
94
+ 'quickfix'
95
+ end
96
+
97
+ # @return [String]
98
+ def title
99
+ "Autocorrect #{cop_name}"
100
+ end
101
+ end
102
+ end
103
+ end
104
+ 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