ruby-lsp 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Build Status](https://github.com/Shopify/ruby-lsp/workflows/CI/badge.svg)
|
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
|