ruby-lsp 0.0.2 → 0.0.3
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +3 -1
- data/.github/workflows/publish_docs.yml +32 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +15 -9
- data/README.md +18 -1
- data/Rakefile +5 -0
- data/VERSION +1 -1
- data/exe/ruby-lsp +1 -0
- data/lib/ruby-lsp.rb +1 -0
- data/lib/ruby_lsp/cli.rb +12 -3
- data/lib/ruby_lsp/document.rb +67 -4
- data/lib/ruby_lsp/handler.rb +51 -3
- data/lib/ruby_lsp/requests/base_request.rb +1 -0
- data/lib/ruby_lsp/requests/code_actions.rb +12 -1
- data/lib/ruby_lsp/requests/diagnostics.rb +17 -78
- data/lib/ruby_lsp/requests/document_symbol.rb +21 -0
- data/lib/ruby_lsp/requests/folding_ranges.rb +9 -0
- data/lib/ruby_lsp/requests/formatting.rb +11 -0
- data/lib/ruby_lsp/requests/rubocop_request.rb +2 -0
- data/lib/ruby_lsp/requests/selection_ranges.rb +102 -0
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +23 -41
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +87 -0
- data/lib/ruby_lsp/requests/support/selection_range.rb +16 -0
- data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +49 -0
- data/lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb +26 -0
- data/lib/ruby_lsp/requests.rb +8 -0
- data/lib/ruby_lsp/store.rb +4 -0
- data/rakelib/check_docs.rake +53 -0
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 58ed87185da0a5d1da74b10f1525f1ca1341f1001f3a4cbdf2b086dd93750ad9
|
4
|
+
data.tar.gz: 1210f736e4cd8450cbe1e1d796dbd22a889c244c6ea5902bc6082f3791064ddc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1ed47a3e4d53dbf5250beaa98b3095c89099e67829f2ecac3149b34ecc87ae579c67ed6cd67035ee9930c0fffa0db6c0d4fdc3c3398520196005b180f28ea6f
|
7
|
+
data.tar.gz: 18d07a7c5426fa8cfad11cfab44dbabf05fd3a1352f1bf9272b04fb35c78f0bb0b4860dd4b7bbfda89efb9cfa99fc3ef77fbb679ff2b21bbd4cffb3c53a14bd5
|
data/.github/workflows/ci.yml
CHANGED
@@ -4,7 +4,7 @@ on: [push, pull_request]
|
|
4
4
|
|
5
5
|
jobs:
|
6
6
|
build:
|
7
|
-
runs-on:
|
7
|
+
runs-on: ubuntu-latest
|
8
8
|
strategy:
|
9
9
|
fail-fast: false
|
10
10
|
matrix:
|
@@ -17,6 +17,8 @@ jobs:
|
|
17
17
|
with:
|
18
18
|
ruby-version: ${{ matrix.ruby }}
|
19
19
|
bundler-cache: true
|
20
|
+
- name: Check if documentation is up to date
|
21
|
+
run: bundle exec rake check_docs
|
20
22
|
- name: Lint Ruby files
|
21
23
|
run: bin/rubocop
|
22
24
|
- name: Run tests
|
@@ -0,0 +1,32 @@
|
|
1
|
+
name: Publish docs
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [main]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
build:
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
name: Publish documentation website
|
11
|
+
steps:
|
12
|
+
- uses: actions/checkout@v3
|
13
|
+
|
14
|
+
- name: Set up Ruby
|
15
|
+
uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: 3.1.1
|
18
|
+
bundler-cache: true
|
19
|
+
|
20
|
+
- name: Configure git
|
21
|
+
run: |
|
22
|
+
git config user.name github-actions
|
23
|
+
git config user.email github-actions@github.com
|
24
|
+
|
25
|
+
- name: Generate documentation
|
26
|
+
run: bundle exec rake yard
|
27
|
+
|
28
|
+
- name: Commit to gh-pages
|
29
|
+
run: |
|
30
|
+
git add docs
|
31
|
+
git commit -m "Publish website $(git log --format=format:%h -1)"
|
32
|
+
git push --force origin main:gh-pages
|
data/Gemfile
CHANGED
@@ -8,6 +8,7 @@ gem "debug", "~> 1.5"
|
|
8
8
|
gem "minitest", "~> 5.15"
|
9
9
|
gem "minitest-reporters", "~> 1.5"
|
10
10
|
gem "rake", "~> 13.0"
|
11
|
-
gem "rubocop-shopify", "~> 2.
|
11
|
+
gem "rubocop-shopify", "~> 2.6"
|
12
12
|
gem "rubocop-minitest", "~> 0.19.1"
|
13
13
|
gem "rubocop-rake", "~> 0.6.0"
|
14
|
+
gem "yard", "~> 0.9"
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ruby-lsp (0.0.
|
4
|
+
ruby-lsp (0.0.3)
|
5
5
|
language_server-protocol
|
6
6
|
rubocop (>= 1.0)
|
7
7
|
syntax_tree (>= 2.3)
|
@@ -28,32 +28,37 @@ GEM
|
|
28
28
|
parallel (1.22.1)
|
29
29
|
parser (3.1.2.0)
|
30
30
|
ast (~> 2.4.1)
|
31
|
+
prettier_print (0.1.0)
|
31
32
|
rainbow (3.1.1)
|
32
33
|
rake (13.0.6)
|
33
|
-
regexp_parser (2.
|
34
|
+
regexp_parser (2.4.0)
|
34
35
|
reline (0.3.1)
|
35
36
|
io-console (~> 0.5)
|
36
37
|
rexml (3.2.5)
|
37
|
-
rubocop (1.
|
38
|
+
rubocop (1.29.1)
|
38
39
|
parallel (~> 1.10)
|
39
40
|
parser (>= 3.1.0.0)
|
40
41
|
rainbow (>= 2.2.2, < 4.0)
|
41
42
|
regexp_parser (>= 1.8, < 3.0)
|
42
|
-
rexml
|
43
|
+
rexml (>= 3.2.5, < 4.0)
|
43
44
|
rubocop-ast (>= 1.17.0, < 2.0)
|
44
45
|
ruby-progressbar (~> 1.7)
|
45
46
|
unicode-display_width (>= 1.4.0, < 3.0)
|
46
|
-
rubocop-ast (1.
|
47
|
+
rubocop-ast (1.18.0)
|
47
48
|
parser (>= 3.1.1.0)
|
48
49
|
rubocop-minitest (0.19.1)
|
49
50
|
rubocop (>= 0.90, < 2.0)
|
50
51
|
rubocop-rake (0.6.0)
|
51
52
|
rubocop (~> 1.0)
|
52
|
-
rubocop-shopify (2.
|
53
|
-
rubocop (~> 1.
|
53
|
+
rubocop-shopify (2.6.0)
|
54
|
+
rubocop (~> 1.29)
|
54
55
|
ruby-progressbar (1.11.0)
|
55
|
-
syntax_tree (2.
|
56
|
+
syntax_tree (2.7.1)
|
57
|
+
prettier_print
|
56
58
|
unicode-display_width (2.1.0)
|
59
|
+
webrick (1.7.0)
|
60
|
+
yard (0.9.27)
|
61
|
+
webrick (~> 1.7.0)
|
57
62
|
|
58
63
|
PLATFORMS
|
59
64
|
arm64-darwin-21
|
@@ -66,8 +71,9 @@ DEPENDENCIES
|
|
66
71
|
rake (~> 13.0)
|
67
72
|
rubocop-minitest (~> 0.19.1)
|
68
73
|
rubocop-rake (~> 0.6.0)
|
69
|
-
rubocop-shopify (~> 2.
|
74
|
+
rubocop-shopify (~> 2.6)
|
70
75
|
ruby-lsp!
|
76
|
+
yard (~> 0.9)
|
71
77
|
|
72
78
|
BUNDLED WITH
|
73
79
|
2.3.7
|
data/README.md
CHANGED
@@ -1,4 +1,21 @@
|
|
1
|
-
|
1
|
+

|
2
|
+
|
3
|
+
# Ruby LSP
|
4
|
+
|
5
|
+
This gem is an implementation of the language server protocol specification for Ruby, used to improve editor features.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
Install the gem. There's no need to require it, since the server is used as a standalone executable.
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
group :development do
|
13
|
+
gem "ruby-lsp", require: false
|
14
|
+
end
|
15
|
+
```
|
16
|
+
|
17
|
+
If using VS Code, install the [Ruby LSP plugin](https://github.com/Shopify/vscode-ruby-lsp) to get the extra features in
|
18
|
+
the editor.
|
2
19
|
|
3
20
|
## Contributing
|
4
21
|
|
data/Rakefile
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "bundler/gem_tasks"
|
4
4
|
require "rake/testtask"
|
5
|
+
require "yard"
|
5
6
|
|
6
7
|
Rake::TestTask.new(:test) do |t|
|
7
8
|
t.libs << "test"
|
@@ -9,6 +10,10 @@ Rake::TestTask.new(:test) do |t|
|
|
9
10
|
t.test_files = FileList["test/**/*_test.rb"]
|
10
11
|
end
|
11
12
|
|
13
|
+
YARD::Rake::YardocTask.new do |t|
|
14
|
+
t.options = ["--markup", "markdown", "--output-dir", "docs"]
|
15
|
+
end
|
16
|
+
|
12
17
|
require "rubocop/rake_task"
|
13
18
|
|
14
19
|
RuboCop::RakeTask.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
data/exe/ruby-lsp
CHANGED
data/lib/ruby-lsp.rb
CHANGED
data/lib/ruby_lsp/cli.rb
CHANGED
@@ -12,13 +12,15 @@ module RubyLsp
|
|
12
12
|
handler.config do
|
13
13
|
on("initialize") do |request|
|
14
14
|
store.clear
|
15
|
-
|
15
|
+
initialization_options = request.dig(:params, :initializationOptions)
|
16
|
+
|
17
|
+
configure_options(initialization_options)
|
18
|
+
respond_with_capabilities(initialization_options.fetch(:enabledFeatures, []))
|
16
19
|
end
|
17
20
|
|
18
21
|
on("textDocument/didChange") do |request|
|
19
22
|
uri = request.dig(:params, :textDocument, :uri)
|
20
|
-
|
21
|
-
store.set(uri, text)
|
23
|
+
store.push_edits(uri, request.dig(:params, :contentChanges))
|
22
24
|
|
23
25
|
send_diagnostics(uri)
|
24
26
|
nil
|
@@ -48,6 +50,13 @@ module RubyLsp
|
|
48
50
|
respond_with_folding_ranges(request.dig(:params, :textDocument, :uri))
|
49
51
|
end
|
50
52
|
|
53
|
+
on("textDocument/selectionRange") do |request|
|
54
|
+
respond_with_selection_ranges(
|
55
|
+
request.dig(:params, :textDocument, :uri),
|
56
|
+
request.dig(:params, :positions),
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
51
60
|
on("textDocument/semanticTokens/full") do |request|
|
52
61
|
respond_with_semantic_highlighting(request.dig(:params, :textDocument, :uri))
|
53
62
|
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "strscan"
|
4
|
+
|
3
5
|
module RubyLsp
|
4
6
|
class Document
|
5
|
-
attr_reader :tree, :
|
7
|
+
attr_reader :tree, :source, :syntax_error_edits
|
6
8
|
|
7
9
|
def initialize(source)
|
8
|
-
@
|
9
|
-
@parser = SyntaxTree::Parser.new(source)
|
10
|
-
@tree = @parser.parse
|
10
|
+
@tree = SyntaxTree.parse(source)
|
11
11
|
@cache = {}
|
12
|
+
@syntax_error_edits = []
|
13
|
+
@source = source
|
14
|
+
@parsable_source = source.dup
|
12
15
|
end
|
13
16
|
|
14
17
|
def ==(other)
|
@@ -23,5 +26,65 @@ module RubyLsp
|
|
23
26
|
@cache[request_name] = result
|
24
27
|
result
|
25
28
|
end
|
29
|
+
|
30
|
+
def push_edits(edits)
|
31
|
+
# Apply the edits on the real source
|
32
|
+
edits.each { |edit| apply_edit(@source, edit[:range], edit[:text]) }
|
33
|
+
|
34
|
+
@cache.clear
|
35
|
+
@tree = SyntaxTree.parse(@source)
|
36
|
+
@syntax_error_edits.clear
|
37
|
+
@parsable_source = @source.dup
|
38
|
+
nil
|
39
|
+
rescue SyntaxTree::Parser::ParseError
|
40
|
+
update_parsable_source(edits)
|
41
|
+
end
|
42
|
+
|
43
|
+
def syntax_errors?
|
44
|
+
@syntax_error_edits.any?
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def update_parsable_source(edits)
|
50
|
+
# If the new edits caused a syntax error, make all edits blank spaces and line breaks to adjust the line and
|
51
|
+
# column numbers. This is attempt to make the document parsable while partial edits are being applied
|
52
|
+
edits.each do |edit|
|
53
|
+
@syntax_error_edits << edit
|
54
|
+
next if edit[:text].empty? # skip deletions, since they may have caused the syntax error
|
55
|
+
|
56
|
+
apply_edit(@parsable_source, edit[:range], edit[:text].gsub(/[^\r\n]/, " "))
|
57
|
+
end
|
58
|
+
|
59
|
+
@tree = SyntaxTree.parse(@parsable_source)
|
60
|
+
rescue SyntaxTree::Parser::ParseError
|
61
|
+
# If we can't parse the source even after emptying the edits, then just fallback to the previous source
|
62
|
+
end
|
63
|
+
|
64
|
+
def apply_edit(source, range, text)
|
65
|
+
scanner = Scanner.new(source)
|
66
|
+
start_position = scanner.find_position(range[:start])
|
67
|
+
end_position = scanner.find_position(range[:end])
|
68
|
+
|
69
|
+
source[start_position...end_position] = text
|
70
|
+
end
|
71
|
+
|
72
|
+
class Scanner
|
73
|
+
def initialize(source)
|
74
|
+
@source = source
|
75
|
+
@scanner = StringScanner.new(source)
|
76
|
+
@current_line = 0
|
77
|
+
end
|
78
|
+
|
79
|
+
def find_position(position)
|
80
|
+
# Move the string scanner counting line breaks until we reach the right line
|
81
|
+
until @current_line == position[:line]
|
82
|
+
@scanner.scan_until(/\R/)
|
83
|
+
@current_line += 1
|
84
|
+
end
|
85
|
+
|
86
|
+
@scanner.pos + position[:character]
|
87
|
+
end
|
88
|
+
end
|
26
89
|
end
|
27
90
|
end
|
data/lib/ruby_lsp/handler.rb
CHANGED
@@ -2,9 +2,15 @@
|
|
2
2
|
|
3
3
|
require "ruby_lsp/requests"
|
4
4
|
require "ruby_lsp/store"
|
5
|
+
require "benchmark"
|
5
6
|
|
6
7
|
module RubyLsp
|
7
8
|
class Handler
|
9
|
+
IGNORED_FOR_TELEMETRY = [
|
10
|
+
"initialized",
|
11
|
+
"$/cancelRequest",
|
12
|
+
].freeze
|
13
|
+
|
8
14
|
attr_reader :store
|
9
15
|
|
10
16
|
Interface = LanguageServer::Protocol::Interface
|
@@ -21,7 +27,7 @@ module RubyLsp
|
|
21
27
|
def start
|
22
28
|
$stderr.puts "Starting Ruby LSP..."
|
23
29
|
@reader.read do |request|
|
24
|
-
handle(request)
|
30
|
+
with_telemetry(request) { handle(request) }
|
25
31
|
end
|
26
32
|
end
|
27
33
|
|
@@ -76,9 +82,10 @@ module RubyLsp
|
|
76
82
|
Interface::InitializeResult.new(
|
77
83
|
capabilities: Interface::ServerCapabilities.new(
|
78
84
|
text_document_sync: Interface::TextDocumentSyncOptions.new(
|
79
|
-
change: Constant::TextDocumentSyncKind::
|
85
|
+
change: Constant::TextDocumentSyncKind::INCREMENTAL,
|
80
86
|
open_close: true,
|
81
87
|
),
|
88
|
+
selection_range_provider: enabled_features.include?("selectionRanges"),
|
82
89
|
document_symbol_provider: document_symbol_provider,
|
83
90
|
folding_range_provider: folding_ranges_provider,
|
84
91
|
semantic_tokens_provider: semantic_tokens_provider,
|
@@ -100,9 +107,25 @@ module RubyLsp
|
|
100
107
|
end
|
101
108
|
end
|
102
109
|
|
110
|
+
def respond_with_selection_ranges(uri, positions)
|
111
|
+
ranges = store.cache_fetch(uri, :selection_ranges) do |document|
|
112
|
+
Requests::SelectionRanges.run(document)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Per the selection range request spec (https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange),
|
116
|
+
# every position in the positions array should have an element at the same index in the response
|
117
|
+
# array. For positions without a valid selection range, the corresponding element in the response
|
118
|
+
# array will be nil.
|
119
|
+
positions.map do |position|
|
120
|
+
ranges.find do |range|
|
121
|
+
range.cover?(position)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
103
126
|
def respond_with_semantic_highlighting(uri)
|
104
127
|
store.cache_fetch(uri, :semantic_highlighting) do |document|
|
105
|
-
Requests::SemanticHighlighting.
|
128
|
+
Requests::SemanticHighlighting.new(document, encoder: Requests::Support::SemanticTokenEncoder.new).run
|
106
129
|
end
|
107
130
|
end
|
108
131
|
|
@@ -129,5 +152,30 @@ module RubyLsp
|
|
129
152
|
Requests::CodeActions.run(uri, document, range)
|
130
153
|
end
|
131
154
|
end
|
155
|
+
|
156
|
+
def configure_options(initialization_options)
|
157
|
+
@telemetry_enabled = initialization_options.fetch(:telemetryEnabled, false)
|
158
|
+
end
|
159
|
+
|
160
|
+
def with_telemetry(request)
|
161
|
+
return yield unless @telemetry_enabled && !IGNORED_FOR_TELEMETRY.include?(request[:method])
|
162
|
+
|
163
|
+
result = nil
|
164
|
+
request_time = Benchmark.realtime do
|
165
|
+
result = yield
|
166
|
+
end
|
167
|
+
|
168
|
+
params = {
|
169
|
+
request: request[:method],
|
170
|
+
requestTime: request_time,
|
171
|
+
lspVersion: RubyLsp::VERSION,
|
172
|
+
}
|
173
|
+
|
174
|
+
uri = request.dig(:params, :textDocument, :uri)
|
175
|
+
params[:uri] = uri if uri
|
176
|
+
|
177
|
+
@writer.write(method: "telemetry/event", params: params)
|
178
|
+
result
|
179
|
+
end
|
132
180
|
end
|
133
181
|
end
|
@@ -2,6 +2,17 @@
|
|
2
2
|
|
3
3
|
module RubyLsp
|
4
4
|
module Requests
|
5
|
+
# The [code actions](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction)
|
6
|
+
# request informs the editor of RuboCop quick fixes that can be applied. These are accesible by hovering over a
|
7
|
+
# specific diagnostic.
|
8
|
+
#
|
9
|
+
# # Example
|
10
|
+
#
|
11
|
+
# ```ruby
|
12
|
+
# def say_hello
|
13
|
+
# puts "Hello" # --> code action: quick fix indentation
|
14
|
+
# end
|
15
|
+
# ```
|
5
16
|
class CodeActions
|
6
17
|
def self.run(uri, document, range)
|
7
18
|
new(uri, document, range).run
|
@@ -16,7 +27,7 @@ module RubyLsp
|
|
16
27
|
def run
|
17
28
|
diagnostics = Diagnostics.run(@uri, @document)
|
18
29
|
corrections = diagnostics.select { |diagnostic| diagnostic.correctable? && diagnostic.in_range?(@range) }
|
19
|
-
return if corrections.empty?
|
30
|
+
return [] if corrections.empty?
|
20
31
|
|
21
32
|
corrections.map!(&:to_lsp_code_action)
|
22
33
|
end
|
@@ -2,95 +2,34 @@
|
|
2
2
|
|
3
3
|
module RubyLsp
|
4
4
|
module Requests
|
5
|
+
# The
|
6
|
+
# [diagnostics](https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics)
|
7
|
+
# request informs the editor of RuboCop offenses for a given file.
|
8
|
+
#
|
9
|
+
# # Example
|
10
|
+
#
|
11
|
+
# ```ruby
|
12
|
+
# def say_hello
|
13
|
+
# puts "Hello" # --> diagnostics: incorrect indentantion
|
14
|
+
# end
|
15
|
+
# ```
|
5
16
|
class Diagnostics < RuboCopRequest
|
6
|
-
RUBOCOP_TO_LSP_SEVERITY = {
|
7
|
-
convention: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
8
|
-
info: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
9
|
-
refactor: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
10
|
-
warning: LanguageServer::Protocol::Constant::DiagnosticSeverity::WARNING,
|
11
|
-
error: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
|
12
|
-
fatal: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
|
13
|
-
}.freeze
|
14
|
-
|
15
17
|
def run
|
18
|
+
return syntax_error_diagnostics if @document.syntax_errors?
|
19
|
+
|
16
20
|
super
|
17
21
|
|
18
22
|
@diagnostics
|
19
23
|
end
|
20
24
|
|
21
25
|
def file_finished(_file, offenses)
|
22
|
-
@diagnostics = offenses.map { |offense|
|
26
|
+
@diagnostics = offenses.map { |offense| Support::RuboCopDiagnostic.new(offense, @uri) }
|
23
27
|
end
|
24
28
|
|
25
|
-
|
26
|
-
attr_reader :replacements
|
27
|
-
|
28
|
-
def initialize(offense, uri)
|
29
|
-
@offense = offense
|
30
|
-
@uri = uri
|
31
|
-
@replacements = offense.correctable? ? offense_replacements : []
|
32
|
-
end
|
33
|
-
|
34
|
-
def correctable?
|
35
|
-
@offense.correctable?
|
36
|
-
end
|
37
|
-
|
38
|
-
def in_range?(range)
|
39
|
-
range.cover?(@offense.line - 1)
|
40
|
-
end
|
41
|
-
|
42
|
-
def to_lsp_code_action
|
43
|
-
LanguageServer::Protocol::Interface::CodeAction.new(
|
44
|
-
title: "Autocorrect #{@offense.cop_name}",
|
45
|
-
kind: LanguageServer::Protocol::Constant::CodeActionKind::QUICK_FIX,
|
46
|
-
edit: LanguageServer::Protocol::Interface::WorkspaceEdit.new(
|
47
|
-
document_changes: [
|
48
|
-
LanguageServer::Protocol::Interface::TextDocumentEdit.new(
|
49
|
-
text_document: LanguageServer::Protocol::Interface::OptionalVersionedTextDocumentIdentifier.new(
|
50
|
-
uri: @uri,
|
51
|
-
version: nil
|
52
|
-
),
|
53
|
-
edits: @replacements
|
54
|
-
),
|
55
|
-
]
|
56
|
-
),
|
57
|
-
is_preferred: true,
|
58
|
-
)
|
59
|
-
end
|
60
|
-
|
61
|
-
def to_lsp_diagnostic
|
62
|
-
LanguageServer::Protocol::Interface::Diagnostic.new(
|
63
|
-
message: @offense.message,
|
64
|
-
source: "RuboCop",
|
65
|
-
code: @offense.cop_name,
|
66
|
-
severity: RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name],
|
67
|
-
range: LanguageServer::Protocol::Interface::Range.new(
|
68
|
-
start: LanguageServer::Protocol::Interface::Position.new(
|
69
|
-
line: @offense.line - 1,
|
70
|
-
character: @offense.column
|
71
|
-
),
|
72
|
-
end: LanguageServer::Protocol::Interface::Position.new(
|
73
|
-
line: @offense.last_line - 1,
|
74
|
-
character: @offense.last_column
|
75
|
-
)
|
76
|
-
)
|
77
|
-
)
|
78
|
-
end
|
79
|
-
|
80
|
-
private
|
29
|
+
private
|
81
30
|
|
82
|
-
|
83
|
-
|
84
|
-
LanguageServer::Protocol::Interface::TextEdit.new(
|
85
|
-
range: LanguageServer::Protocol::Interface::Range.new(
|
86
|
-
start: LanguageServer::Protocol::Interface::Position.new(line: range.line - 1, character: range.column),
|
87
|
-
end: LanguageServer::Protocol::Interface::Position.new(line: range.last_line - 1,
|
88
|
-
character: range.last_column)
|
89
|
-
),
|
90
|
-
new_text: replacement
|
91
|
-
)
|
92
|
-
end
|
93
|
-
end
|
31
|
+
def syntax_error_diagnostics
|
32
|
+
@document.syntax_error_edits.map { |e| Support::SyntaxErrorDiagnostic.new(e) }
|
94
33
|
end
|
95
34
|
end
|
96
35
|
end
|
@@ -2,6 +2,27 @@
|
|
2
2
|
|
3
3
|
module RubyLsp
|
4
4
|
module Requests
|
5
|
+
# The [document
|
6
|
+
# symbol](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol) request
|
7
|
+
# informs the editor of all the important symbols, such as classes, variables, and methods, defined in a file. With
|
8
|
+
# this information, the editor can populate breadcrumbs, file outline and allow for fuzzy symbol searches.
|
9
|
+
#
|
10
|
+
# In VS Code, fuzzy symbol search can be accessed by opened the command palette and inserting an `@` symbol.
|
11
|
+
#
|
12
|
+
# # Example
|
13
|
+
#
|
14
|
+
# ```ruby
|
15
|
+
# class Person # --> document symbol: class
|
16
|
+
# attr_reader :age # --> document symbol: field
|
17
|
+
#
|
18
|
+
# def initialize
|
19
|
+
# @age = 0 # --> document symbol: variable
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def age # --> document symbol: method
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
# ```
|
5
26
|
class DocumentSymbol < BaseRequest
|
6
27
|
SYMBOL_KIND = {
|
7
28
|
file: 1,
|
@@ -2,6 +2,15 @@
|
|
2
2
|
|
3
3
|
module RubyLsp
|
4
4
|
module Requests
|
5
|
+
# The [folding ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange)
|
6
|
+
# request informs the editor of the ranges where code can be folded.
|
7
|
+
#
|
8
|
+
# # Example
|
9
|
+
# ```ruby
|
10
|
+
# def say_hello # <-- folding range start
|
11
|
+
# puts "Hello"
|
12
|
+
# end # <-- folding range end
|
13
|
+
# ```
|
5
14
|
class FoldingRanges < BaseRequest
|
6
15
|
SIMPLE_FOLDABLES = [
|
7
16
|
SyntaxTree::ArrayLiteral,
|
@@ -2,6 +2,17 @@
|
|
2
2
|
|
3
3
|
module RubyLsp
|
4
4
|
module Requests
|
5
|
+
# The [formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting)
|
6
|
+
# request uses RuboCop to fix auto-correctable offenses in the document. This requires enabling format on save and
|
7
|
+
# registering the ruby-lsp as the Ruby formatter.
|
8
|
+
#
|
9
|
+
# # Example
|
10
|
+
#
|
11
|
+
# ```ruby
|
12
|
+
# def say_hello
|
13
|
+
# puts "Hello" # --> formatting: fixes the indentation on save
|
14
|
+
# end
|
15
|
+
# ```
|
5
16
|
class Formatting < RuboCopRequest
|
6
17
|
RUBOCOP_FLAGS = (COMMON_RUBOCOP_FLAGS + ["--auto-correct"]).freeze
|
7
18
|
|
@@ -5,6 +5,7 @@ require "cgi"
|
|
5
5
|
|
6
6
|
module RubyLsp
|
7
7
|
module Requests
|
8
|
+
# :nodoc:
|
8
9
|
class RuboCopRequest < RuboCop::Runner
|
9
10
|
COMMON_RUBOCOP_FLAGS = [
|
10
11
|
"--stderr", # Print any output to stderr so that our stdout does not get polluted
|
@@ -20,6 +21,7 @@ module RubyLsp
|
|
20
21
|
|
21
22
|
def initialize(uri, document)
|
22
23
|
@file = CGI.unescape(URI.parse(uri).path)
|
24
|
+
@document = document
|
23
25
|
@text = document.source
|
24
26
|
@uri = uri
|
25
27
|
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLsp
|
4
|
+
module Requests
|
5
|
+
# The [selection ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange)
|
6
|
+
# request informs the editor of ranges that the user may want to select based on the location(s)
|
7
|
+
# of their cursor(s).
|
8
|
+
#
|
9
|
+
# Trigger this request with: Ctrl + Shift + -> or Ctrl + Shift + <-
|
10
|
+
#
|
11
|
+
# # Example
|
12
|
+
#
|
13
|
+
# ```ruby
|
14
|
+
# def foo # --> The next selection range encompasses the entire method definition.
|
15
|
+
# puts "Hello, world!" # --> Cursor is on this line
|
16
|
+
# end
|
17
|
+
# ```
|
18
|
+
class SelectionRanges < BaseRequest
|
19
|
+
NODES_THAT_CAN_BE_PARENTS = [
|
20
|
+
SyntaxTree::Assign,
|
21
|
+
SyntaxTree::ArrayLiteral,
|
22
|
+
SyntaxTree::Begin,
|
23
|
+
SyntaxTree::BraceBlock,
|
24
|
+
SyntaxTree::Call,
|
25
|
+
SyntaxTree::Case,
|
26
|
+
SyntaxTree::ClassDeclaration,
|
27
|
+
SyntaxTree::Command,
|
28
|
+
SyntaxTree::Def,
|
29
|
+
SyntaxTree::Defs,
|
30
|
+
SyntaxTree::DoBlock,
|
31
|
+
SyntaxTree::Elsif,
|
32
|
+
SyntaxTree::Else,
|
33
|
+
SyntaxTree::EmbDoc,
|
34
|
+
SyntaxTree::Ensure,
|
35
|
+
SyntaxTree::FCall,
|
36
|
+
SyntaxTree::For,
|
37
|
+
SyntaxTree::HashLiteral,
|
38
|
+
SyntaxTree::Heredoc,
|
39
|
+
SyntaxTree::HeredocBeg,
|
40
|
+
SyntaxTree::HshPtn,
|
41
|
+
SyntaxTree::If,
|
42
|
+
SyntaxTree::In,
|
43
|
+
SyntaxTree::Lambda,
|
44
|
+
SyntaxTree::MethodAddBlock,
|
45
|
+
SyntaxTree::ModuleDeclaration,
|
46
|
+
SyntaxTree::Params,
|
47
|
+
SyntaxTree::Rescue,
|
48
|
+
SyntaxTree::RescueEx,
|
49
|
+
SyntaxTree::StringConcat,
|
50
|
+
SyntaxTree::StringLiteral,
|
51
|
+
SyntaxTree::Unless,
|
52
|
+
SyntaxTree::Until,
|
53
|
+
SyntaxTree::VCall,
|
54
|
+
SyntaxTree::When,
|
55
|
+
SyntaxTree::While,
|
56
|
+
].freeze
|
57
|
+
|
58
|
+
def initialize(document)
|
59
|
+
super(document)
|
60
|
+
|
61
|
+
@ranges = []
|
62
|
+
@stack = []
|
63
|
+
end
|
64
|
+
|
65
|
+
def run
|
66
|
+
visit(@document.tree)
|
67
|
+
@ranges.reverse!
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def visit(node)
|
73
|
+
return if node.nil?
|
74
|
+
|
75
|
+
range = create_selection_range(node.location, @stack.last)
|
76
|
+
|
77
|
+
@ranges << range
|
78
|
+
return if node.child_nodes.empty?
|
79
|
+
|
80
|
+
@stack << range if NODES_THAT_CAN_BE_PARENTS.include?(node.class)
|
81
|
+
visit_all(node.child_nodes)
|
82
|
+
@stack.pop if NODES_THAT_CAN_BE_PARENTS.include?(node.class)
|
83
|
+
end
|
84
|
+
|
85
|
+
def create_selection_range(location, parent = nil)
|
86
|
+
RubyLsp::Requests::Support::SelectionRange.new(
|
87
|
+
range: LanguageServer::Protocol::Interface::Range.new(
|
88
|
+
start: LanguageServer::Protocol::Interface::Position.new(
|
89
|
+
line: location.start_line - 1,
|
90
|
+
character: location.start_column,
|
91
|
+
),
|
92
|
+
end: LanguageServer::Protocol::Interface::Position.new(
|
93
|
+
line: location.end_line - 1,
|
94
|
+
character: location.end_column,
|
95
|
+
),
|
96
|
+
),
|
97
|
+
parent: parent
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -2,6 +2,19 @@
|
|
2
2
|
|
3
3
|
module RubyLsp
|
4
4
|
module Requests
|
5
|
+
# The [semantic
|
6
|
+
# highlighting](https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens)
|
7
|
+
# request informs the editor of the correct token types to provide consistent and accurate highlighting for themes.
|
8
|
+
#
|
9
|
+
# # Example
|
10
|
+
#
|
11
|
+
# ```ruby
|
12
|
+
# def foo
|
13
|
+
# var = 1 # --> semantic highlighting: local variable
|
14
|
+
# some_invocation # --> semantic highlighting: method invocation
|
15
|
+
# var # --> semantic highlighting: local variable
|
16
|
+
# end
|
17
|
+
# ```
|
5
18
|
class SemanticHighlighting < BaseRequest
|
6
19
|
TOKEN_TYPES = [
|
7
20
|
:variable,
|
@@ -9,18 +22,21 @@ module RubyLsp
|
|
9
22
|
].freeze
|
10
23
|
TOKEN_MODIFIERS = [].freeze
|
11
24
|
|
12
|
-
|
13
|
-
super
|
25
|
+
SemanticToken = Struct.new(:location, :length, :type, :modifier)
|
14
26
|
|
27
|
+
def initialize(document, encoder: nil)
|
28
|
+
super(document)
|
29
|
+
|
30
|
+
@encoder = encoder
|
15
31
|
@tokens = []
|
16
32
|
@tree = document.tree
|
17
|
-
@current_row = 0
|
18
|
-
@current_column = 0
|
19
33
|
end
|
20
34
|
|
21
35
|
def run
|
22
36
|
visit(@tree)
|
23
|
-
|
37
|
+
return @tokens unless @encoder
|
38
|
+
|
39
|
+
@encoder.encode(@tokens)
|
24
40
|
end
|
25
41
|
|
26
42
|
def visit_m_assign(node)
|
@@ -73,44 +89,10 @@ module RubyLsp
|
|
73
89
|
add_token(node.value.location, :method)
|
74
90
|
end
|
75
91
|
|
76
|
-
def add_token(location,
|
92
|
+
def add_token(location, type)
|
77
93
|
length = location.end_char - location.start_char
|
78
|
-
|
79
|
-
compute_delta(location) do |delta_line, delta_column|
|
80
|
-
@tokens.push(delta_line, delta_column, length, TOKEN_TYPES.index(classification), 0)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
# The delta array is computed according to the LSP specification:
|
85
|
-
# > The protocol for the token format relative uses relative
|
86
|
-
# > positions, because most tokens remain stable relative to
|
87
|
-
# > each other when edits are made in a file. This simplifies
|
88
|
-
# > the computation of a delta if a server supports it. So each
|
89
|
-
# > token is represented using 5 integers.
|
90
|
-
|
91
|
-
# For more information on how each number is calculated, read:
|
92
|
-
# https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens
|
93
|
-
def compute_delta(location)
|
94
|
-
row = location.start_line - 1
|
95
|
-
column = location.start_column
|
96
|
-
|
97
|
-
if row < @current_row
|
98
|
-
raise InvalidTokenRowError, "Invalid token row detected: " \
|
99
|
-
"Ensure tokens are added in the expected order."
|
100
|
-
end
|
101
|
-
|
102
|
-
delta_line = row - @current_row
|
103
|
-
|
104
|
-
delta_column = column
|
105
|
-
delta_column -= @current_column if delta_line == 0
|
106
|
-
|
107
|
-
yield delta_line, delta_column
|
108
|
-
|
109
|
-
@current_row = row
|
110
|
-
@current_column = column
|
94
|
+
@tokens.push(SemanticToken.new(location, length, TOKEN_TYPES.index(type), 0))
|
111
95
|
end
|
112
|
-
|
113
|
-
class InvalidTokenRowError < StandardError; end
|
114
96
|
end
|
115
97
|
end
|
116
98
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLsp
|
4
|
+
module Requests
|
5
|
+
module Support
|
6
|
+
class RuboCopDiagnostic
|
7
|
+
RUBOCOP_TO_LSP_SEVERITY = {
|
8
|
+
convention: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
9
|
+
info: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
10
|
+
refactor: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
11
|
+
warning: LanguageServer::Protocol::Constant::DiagnosticSeverity::WARNING,
|
12
|
+
error: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
|
13
|
+
fatal: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
attr_reader :replacements
|
17
|
+
|
18
|
+
def initialize(offense, uri)
|
19
|
+
@offense = offense
|
20
|
+
@uri = uri
|
21
|
+
@replacements = offense.correctable? ? offense_replacements : []
|
22
|
+
end
|
23
|
+
|
24
|
+
def correctable?
|
25
|
+
@offense.correctable?
|
26
|
+
end
|
27
|
+
|
28
|
+
def in_range?(range)
|
29
|
+
range.cover?(@offense.line - 1)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_lsp_code_action
|
33
|
+
LanguageServer::Protocol::Interface::CodeAction.new(
|
34
|
+
title: "Autocorrect #{@offense.cop_name}",
|
35
|
+
kind: LanguageServer::Protocol::Constant::CodeActionKind::QUICK_FIX,
|
36
|
+
edit: LanguageServer::Protocol::Interface::WorkspaceEdit.new(
|
37
|
+
document_changes: [
|
38
|
+
LanguageServer::Protocol::Interface::TextDocumentEdit.new(
|
39
|
+
text_document: LanguageServer::Protocol::Interface::OptionalVersionedTextDocumentIdentifier.new(
|
40
|
+
uri: @uri,
|
41
|
+
version: nil
|
42
|
+
),
|
43
|
+
edits: @replacements
|
44
|
+
),
|
45
|
+
]
|
46
|
+
),
|
47
|
+
is_preferred: true,
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_lsp_diagnostic
|
52
|
+
LanguageServer::Protocol::Interface::Diagnostic.new(
|
53
|
+
message: @offense.message,
|
54
|
+
source: "RuboCop",
|
55
|
+
code: @offense.cop_name,
|
56
|
+
severity: RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name],
|
57
|
+
range: LanguageServer::Protocol::Interface::Range.new(
|
58
|
+
start: LanguageServer::Protocol::Interface::Position.new(
|
59
|
+
line: @offense.line - 1,
|
60
|
+
character: @offense.column
|
61
|
+
),
|
62
|
+
end: LanguageServer::Protocol::Interface::Position.new(
|
63
|
+
line: @offense.last_line - 1,
|
64
|
+
character: @offense.last_column
|
65
|
+
)
|
66
|
+
)
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def offense_replacements
|
73
|
+
@offense.corrector.as_replacements.map do |range, replacement|
|
74
|
+
LanguageServer::Protocol::Interface::TextEdit.new(
|
75
|
+
range: LanguageServer::Protocol::Interface::Range.new(
|
76
|
+
start: LanguageServer::Protocol::Interface::Position.new(line: range.line - 1, character: range.column),
|
77
|
+
end: LanguageServer::Protocol::Interface::Position.new(line: range.last_line - 1,
|
78
|
+
character: range.last_column)
|
79
|
+
),
|
80
|
+
new_text: replacement
|
81
|
+
)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLsp
|
4
|
+
module Requests
|
5
|
+
module Support
|
6
|
+
class SelectionRange < LanguageServer::Protocol::Interface::SelectionRange
|
7
|
+
def cover?(position)
|
8
|
+
line_range = (range.start.line..range.end.line)
|
9
|
+
character_range = (range.start.character..range.end.character)
|
10
|
+
|
11
|
+
line_range.cover?(position[:line]) && character_range.cover?(position[:character])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLsp
|
4
|
+
module Requests
|
5
|
+
module Support
|
6
|
+
class SemanticTokenEncoder
|
7
|
+
def initialize
|
8
|
+
@current_row = 0
|
9
|
+
@current_column = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def encode(tokens)
|
13
|
+
delta = tokens
|
14
|
+
.sort_by do |token|
|
15
|
+
[token.location.start_line, token.location.start_column]
|
16
|
+
end
|
17
|
+
.flat_map do |token|
|
18
|
+
compute_delta(token)
|
19
|
+
end
|
20
|
+
|
21
|
+
LanguageServer::Protocol::Interface::SemanticTokens.new(data: delta)
|
22
|
+
end
|
23
|
+
|
24
|
+
# The delta array is computed according to the LSP specification:
|
25
|
+
# > The protocol for the token format relative uses relative
|
26
|
+
# > positions, because most tokens remain stable relative to
|
27
|
+
# > each other when edits are made in a file. This simplifies
|
28
|
+
# > the computation of a delta if a server supports it. So each
|
29
|
+
# > token is represented using 5 integers.
|
30
|
+
|
31
|
+
# For more information on how each number is calculated, read:
|
32
|
+
# https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens
|
33
|
+
def compute_delta(token)
|
34
|
+
row = token.location.start_line - 1
|
35
|
+
column = token.location.start_column
|
36
|
+
delta_line = row - @current_row
|
37
|
+
|
38
|
+
delta_column = column
|
39
|
+
delta_column -= @current_column if delta_line == 0
|
40
|
+
|
41
|
+
[delta_line, delta_column, token.length, token.type, token.modifier]
|
42
|
+
ensure
|
43
|
+
@current_row = row
|
44
|
+
@current_column = column
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLsp
|
4
|
+
module Requests
|
5
|
+
module Support
|
6
|
+
class SyntaxErrorDiagnostic
|
7
|
+
def initialize(edit)
|
8
|
+
@edit = edit
|
9
|
+
end
|
10
|
+
|
11
|
+
def correctable?
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_lsp_diagnostic
|
16
|
+
LanguageServer::Protocol::Interface::Diagnostic.new(
|
17
|
+
message: "Syntax error",
|
18
|
+
source: "SyntaxTree",
|
19
|
+
severity: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
|
20
|
+
range: @edit[:range]
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/ruby_lsp/requests.rb
CHANGED
@@ -5,10 +5,18 @@ module RubyLsp
|
|
5
5
|
autoload :BaseRequest, "ruby_lsp/requests/base_request"
|
6
6
|
autoload :DocumentSymbol, "ruby_lsp/requests/document_symbol"
|
7
7
|
autoload :FoldingRanges, "ruby_lsp/requests/folding_ranges"
|
8
|
+
autoload :SelectionRanges, "ruby_lsp/requests/selection_ranges"
|
8
9
|
autoload :SemanticHighlighting, "ruby_lsp/requests/semantic_highlighting"
|
9
10
|
autoload :RuboCopRequest, "ruby_lsp/requests/rubocop_request"
|
10
11
|
autoload :Formatting, "ruby_lsp/requests/formatting"
|
11
12
|
autoload :Diagnostics, "ruby_lsp/requests/diagnostics"
|
12
13
|
autoload :CodeActions, "ruby_lsp/requests/code_actions"
|
14
|
+
|
15
|
+
module Support
|
16
|
+
autoload :RuboCopDiagnostic, "ruby_lsp/requests/support/rubocop_diagnostic"
|
17
|
+
autoload :SelectionRange, "ruby_lsp/requests/support/selection_range"
|
18
|
+
autoload :SemanticTokenEncoder, "ruby_lsp/requests/support/semantic_token_encoder"
|
19
|
+
autoload :SyntaxErrorDiagnostic, "ruby_lsp/requests/support/syntax_error_diagnostic"
|
20
|
+
end
|
13
21
|
end
|
14
22
|
end
|
data/lib/ruby_lsp/store.rb
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
desc "Check if all LSP requests are documented"
|
4
|
+
task :check_docs do
|
5
|
+
require "language_server-protocol"
|
6
|
+
require "syntax_tree"
|
7
|
+
require "logger"
|
8
|
+
require "ruby_lsp/requests/base_request"
|
9
|
+
require "ruby_lsp/requests/rubocop_request"
|
10
|
+
|
11
|
+
Dir["#{Dir.pwd}/lib/ruby_lsp/requests/*.rb"].each do |file|
|
12
|
+
require(file)
|
13
|
+
YARD.parse(file, [], Logger::Severity::FATAL)
|
14
|
+
end
|
15
|
+
|
16
|
+
spec_matcher = %r{\(https://microsoft.github.io/language-server-protocol/specification#.*\)}
|
17
|
+
error_messages = RubyLsp::Requests.constants.each_with_object(Hash.new { |h, k| h[k] = [] }) do |request, errors|
|
18
|
+
full_name = "RubyLsp::Requests::#{request}"
|
19
|
+
docs = YARD::Registry.at(full_name).docstring
|
20
|
+
next if /:nodoc:/.match?(docs)
|
21
|
+
|
22
|
+
if docs.empty?
|
23
|
+
errors[full_name] << "Missing documentation for request handler class"
|
24
|
+
elsif !spec_matcher.match?(docs)
|
25
|
+
errors[full_name] << <<~MESSAGE
|
26
|
+
Documentation for request handler classes must link to the official LSP specification.
|
27
|
+
|
28
|
+
For example, if your request handles text document hover, you should add a link to
|
29
|
+
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover.
|
30
|
+
MESSAGE
|
31
|
+
elsif !/# Example/.match?(docs)
|
32
|
+
errors[full_name] << <<~MESSAGE
|
33
|
+
Documentation for request handler class must contain an example.
|
34
|
+
|
35
|
+
= Example
|
36
|
+
def my_method # <-- something happens here
|
37
|
+
end
|
38
|
+
MESSAGE
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
formatted_errors = error_messages.map { |name, errors| "#{name}: #{errors.join(", ")}" }
|
43
|
+
|
44
|
+
if error_messages.any?
|
45
|
+
puts <<~MESSAGE
|
46
|
+
The following requests have invalid documentation:
|
47
|
+
|
48
|
+
#{formatted_errors.join("\n")}
|
49
|
+
MESSAGE
|
50
|
+
|
51
|
+
exit!
|
52
|
+
end
|
53
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -64,6 +64,7 @@ files:
|
|
64
64
|
- ".github/probots.yml"
|
65
65
|
- ".github/pull_request_template.md"
|
66
66
|
- ".github/workflows/ci.yml"
|
67
|
+
- ".github/workflows/publish_docs.yml"
|
67
68
|
- ".gitignore"
|
68
69
|
- ".rubocop.yml"
|
69
70
|
- ".vscode/extensions.json"
|
@@ -93,8 +94,14 @@ files:
|
|
93
94
|
- lib/ruby_lsp/requests/folding_ranges.rb
|
94
95
|
- lib/ruby_lsp/requests/formatting.rb
|
95
96
|
- lib/ruby_lsp/requests/rubocop_request.rb
|
97
|
+
- lib/ruby_lsp/requests/selection_ranges.rb
|
96
98
|
- lib/ruby_lsp/requests/semantic_highlighting.rb
|
99
|
+
- lib/ruby_lsp/requests/support/rubocop_diagnostic.rb
|
100
|
+
- lib/ruby_lsp/requests/support/selection_range.rb
|
101
|
+
- lib/ruby_lsp/requests/support/semantic_token_encoder.rb
|
102
|
+
- lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb
|
97
103
|
- lib/ruby_lsp/store.rb
|
104
|
+
- rakelib/check_docs.rake
|
98
105
|
- ruby-lsp.gemspec
|
99
106
|
- service.yml
|
100
107
|
- shipit.production.yml
|