ruby-lsp 0.0.1 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +7 -16
- data/.github/pull_request_template.md +15 -0
- data/.github/workflows/ci.yml +31 -0
- data/.github/workflows/publish_docs.yml +32 -0
- data/.gitignore +9 -12
- data/.rubocop.yml +20 -2
- data/.vscode/settings.json +5 -0
- data/CHANGELOG.md +29 -0
- data/Gemfile +8 -4
- data/Gemfile.lock +76 -14
- data/README.md +69 -2
- data/Rakefile +5 -0
- data/VERSION +1 -1
- data/bin/tapioca +29 -0
- data/bin/test +7 -1
- data/dev.yml +7 -7
- data/exe/ruby-lsp +19 -2
- data/lib/internal.rb +7 -0
- data/lib/ruby-lsp.rb +4 -1
- data/lib/ruby_lsp/cli.rb +88 -0
- data/lib/ruby_lsp/document.rb +113 -0
- data/lib/ruby_lsp/handler.rb +236 -0
- data/lib/ruby_lsp/requests/base_request.rb +33 -0
- data/lib/ruby_lsp/requests/code_actions.rb +37 -0
- data/lib/ruby_lsp/requests/diagnostics.rb +37 -0
- data/lib/ruby_lsp/requests/document_highlight.rb +96 -0
- data/lib/ruby_lsp/requests/document_symbol.rb +216 -0
- data/lib/ruby_lsp/requests/folding_ranges.rb +213 -0
- data/lib/ruby_lsp/requests/formatting.rb +52 -0
- data/lib/ruby_lsp/requests/rubocop_request.rb +50 -0
- data/lib/ruby_lsp/requests/selection_ranges.rb +103 -0
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +112 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +88 -0
- data/lib/ruby_lsp/requests/support/selection_range.rb +17 -0
- data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +60 -0
- data/lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb +27 -0
- data/lib/ruby_lsp/requests.rb +24 -0
- data/lib/ruby_lsp/store.rb +59 -0
- data/rakelib/check_docs.rake +56 -0
- data/ruby-lsp.gemspec +5 -1
- data/{shipit.yml → shipit.production.yml} +0 -0
- data/sorbet/config +4 -0
- data/sorbet/rbi/.rubocop.yml +8 -0
- data/sorbet/rbi/gems/ansi@1.5.0.rbi +338 -0
- data/sorbet/rbi/gems/ast@2.4.2.rbi +522 -0
- data/sorbet/rbi/gems/builder@3.2.4.rbi +418 -0
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
- data/sorbet/rbi/gems/debug@1.5.0.rbi +1273 -0
- data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +867 -0
- data/sorbet/rbi/gems/io-console@0.5.11.rbi +8 -0
- data/sorbet/rbi/gems/irb@1.4.1.rbi +376 -0
- data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +7325 -0
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +8 -0
- data/sorbet/rbi/gems/minitest-reporters@1.5.0.rbi +612 -0
- data/sorbet/rbi/gems/minitest@5.15.0.rbi +994 -0
- data/sorbet/rbi/gems/parallel@1.22.1.rbi +163 -0
- data/sorbet/rbi/gems/parser@3.1.2.0.rbi +3968 -0
- data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +734 -0
- data/sorbet/rbi/gems/pry@0.14.1.rbi +8 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +227 -0
- data/sorbet/rbi/gems/rake@13.0.6.rbi +1853 -0
- data/sorbet/rbi/gems/rbi@0.0.14.rbi +2337 -0
- data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +1854 -0
- data/sorbet/rbi/gems/reline@0.3.1.rbi +1274 -0
- data/sorbet/rbi/gems/rexml@3.2.5.rbi +3852 -0
- data/sorbet/rbi/gems/rubocop-ast@1.18.0.rbi +4180 -0
- data/sorbet/rbi/gems/rubocop-minitest@0.20.0.rbi +1369 -0
- data/sorbet/rbi/gems/rubocop-rake@0.6.0.rbi +246 -0
- data/sorbet/rbi/gems/rubocop-shopify@2.6.0.rbi +8 -0
- data/sorbet/rbi/gems/rubocop-sorbet@0.6.8.rbi +652 -0
- data/sorbet/rbi/gems/rubocop@1.30.0.rbi +36729 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +732 -0
- data/sorbet/rbi/gems/spoom@1.1.11.rbi +1600 -0
- data/sorbet/rbi/gems/syntax_tree@2.7.1.rbi +6777 -0
- data/sorbet/rbi/gems/tapioca@0.8.1.rbi +1972 -0
- data/sorbet/rbi/gems/thor@1.2.1.rbi +2921 -0
- data/sorbet/rbi/gems/unicode-display_width@2.1.0.rbi +27 -0
- data/sorbet/rbi/gems/unparser@0.6.5.rbi +2789 -0
- data/sorbet/rbi/gems/webrick@1.7.0.rbi +1779 -0
- data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +289 -0
- data/sorbet/rbi/gems/yard@0.9.27.rbi +13048 -0
- data/sorbet/rbi/shims/fiddle.rbi +4 -0
- data/sorbet/rbi/shims/hash.rbi +6 -0
- data/sorbet/rbi/shims/rdoc.rbi +4 -0
- data/sorbet/tapioca/config.yml +13 -0
- data/sorbet/tapioca/require.rb +7 -0
- metadata +119 -9
- data/.vscode/launch.json +0 -19
- data/bin/package_extension +0 -5
- data/bin/style +0 -10
- data/lib/ruby/lsp/cli.rb +0 -37
- data/lib/ruby/lsp.rb +0 -3
@@ -0,0 +1,112 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
# The [semantic
|
7
|
+
# highlighting](https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens)
|
8
|
+
# request informs the editor of the correct token types to provide consistent and accurate highlighting for themes.
|
9
|
+
#
|
10
|
+
# # Example
|
11
|
+
#
|
12
|
+
# ```ruby
|
13
|
+
# def foo
|
14
|
+
# var = 1 # --> semantic highlighting: local variable
|
15
|
+
# some_invocation # --> semantic highlighting: method invocation
|
16
|
+
# var # --> semantic highlighting: local variable
|
17
|
+
# end
|
18
|
+
# ```
|
19
|
+
class SemanticHighlighting < BaseRequest
|
20
|
+
TOKEN_TYPES = [
|
21
|
+
:variable,
|
22
|
+
:method,
|
23
|
+
].freeze
|
24
|
+
|
25
|
+
TOKEN_MODIFIERS = {
|
26
|
+
declaration: 0,
|
27
|
+
definition: 1,
|
28
|
+
readonly: 2,
|
29
|
+
static: 3,
|
30
|
+
deprecated: 4,
|
31
|
+
abstract: 5,
|
32
|
+
async: 6,
|
33
|
+
modification: 7,
|
34
|
+
documentation: 8,
|
35
|
+
default_library: 9,
|
36
|
+
}.freeze
|
37
|
+
|
38
|
+
SemanticToken = Struct.new(:location, :length, :type, :modifier)
|
39
|
+
|
40
|
+
def initialize(document, encoder: nil)
|
41
|
+
super(document)
|
42
|
+
|
43
|
+
@encoder = encoder
|
44
|
+
@tokens = []
|
45
|
+
@tree = document.tree
|
46
|
+
end
|
47
|
+
|
48
|
+
def run
|
49
|
+
visit(@tree)
|
50
|
+
return @tokens unless @encoder
|
51
|
+
|
52
|
+
@encoder.encode(@tokens)
|
53
|
+
end
|
54
|
+
|
55
|
+
def visit_m_assign(node)
|
56
|
+
node.target.parts.each do |var_ref|
|
57
|
+
add_token(var_ref.value.location, :variable)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def visit_var_field(node)
|
62
|
+
case node.value
|
63
|
+
when SyntaxTree::Ident
|
64
|
+
add_token(node.value.location, :variable)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def visit_var_ref(node)
|
69
|
+
case node.value
|
70
|
+
when SyntaxTree::Ident
|
71
|
+
add_token(node.value.location, :variable)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def visit_a_ref_field(node)
|
76
|
+
add_token(node.collection.value.location, :variable)
|
77
|
+
end
|
78
|
+
|
79
|
+
def visit_call(node)
|
80
|
+
visit(node.receiver)
|
81
|
+
add_token(node.message.location, :method)
|
82
|
+
visit(node.arguments)
|
83
|
+
end
|
84
|
+
|
85
|
+
def visit_command(node)
|
86
|
+
add_token(node.message.location, :method)
|
87
|
+
visit(node.arguments)
|
88
|
+
end
|
89
|
+
|
90
|
+
def visit_command_call(node)
|
91
|
+
visit(node.receiver)
|
92
|
+
add_token(node.message.location, :method)
|
93
|
+
visit(node.arguments)
|
94
|
+
end
|
95
|
+
|
96
|
+
def visit_fcall(node)
|
97
|
+
add_token(node.value.location, :method)
|
98
|
+
visit(node.arguments)
|
99
|
+
end
|
100
|
+
|
101
|
+
def visit_vcall(node)
|
102
|
+
add_token(node.value.location, :method)
|
103
|
+
end
|
104
|
+
|
105
|
+
def add_token(location, type, modifiers = [])
|
106
|
+
length = location.end_char - location.start_char
|
107
|
+
modifiers_indices = modifiers.filter_map { |modifier| TOKEN_MODIFIERS[modifier] }
|
108
|
+
@tokens.push(SemanticToken.new(location, length, TOKEN_TYPES.index(type), modifiers_indices))
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
module Support
|
7
|
+
class RuboCopDiagnostic
|
8
|
+
RUBOCOP_TO_LSP_SEVERITY = {
|
9
|
+
convention: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
10
|
+
info: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
11
|
+
refactor: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
12
|
+
warning: LanguageServer::Protocol::Constant::DiagnosticSeverity::WARNING,
|
13
|
+
error: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
|
14
|
+
fatal: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
attr_reader :replacements
|
18
|
+
|
19
|
+
def initialize(offense, uri)
|
20
|
+
@offense = offense
|
21
|
+
@uri = uri
|
22
|
+
@replacements = offense.correctable? ? offense_replacements : []
|
23
|
+
end
|
24
|
+
|
25
|
+
def correctable?
|
26
|
+
@offense.correctable?
|
27
|
+
end
|
28
|
+
|
29
|
+
def in_range?(range)
|
30
|
+
range.cover?(@offense.line - 1)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_lsp_code_action
|
34
|
+
LanguageServer::Protocol::Interface::CodeAction.new(
|
35
|
+
title: "Autocorrect #{@offense.cop_name}",
|
36
|
+
kind: LanguageServer::Protocol::Constant::CodeActionKind::QUICK_FIX,
|
37
|
+
edit: LanguageServer::Protocol::Interface::WorkspaceEdit.new(
|
38
|
+
document_changes: [
|
39
|
+
LanguageServer::Protocol::Interface::TextDocumentEdit.new(
|
40
|
+
text_document: LanguageServer::Protocol::Interface::OptionalVersionedTextDocumentIdentifier.new(
|
41
|
+
uri: @uri,
|
42
|
+
version: nil
|
43
|
+
),
|
44
|
+
edits: @replacements
|
45
|
+
),
|
46
|
+
]
|
47
|
+
),
|
48
|
+
is_preferred: true,
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_lsp_diagnostic
|
53
|
+
LanguageServer::Protocol::Interface::Diagnostic.new(
|
54
|
+
message: @offense.message,
|
55
|
+
source: "RuboCop",
|
56
|
+
code: @offense.cop_name,
|
57
|
+
severity: RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name],
|
58
|
+
range: LanguageServer::Protocol::Interface::Range.new(
|
59
|
+
start: LanguageServer::Protocol::Interface::Position.new(
|
60
|
+
line: @offense.line - 1,
|
61
|
+
character: @offense.column
|
62
|
+
),
|
63
|
+
end: LanguageServer::Protocol::Interface::Position.new(
|
64
|
+
line: @offense.last_line - 1,
|
65
|
+
character: @offense.last_column
|
66
|
+
)
|
67
|
+
)
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def offense_replacements
|
74
|
+
@offense.corrector.as_replacements.map do |range, replacement|
|
75
|
+
LanguageServer::Protocol::Interface::TextEdit.new(
|
76
|
+
range: LanguageServer::Protocol::Interface::Range.new(
|
77
|
+
start: LanguageServer::Protocol::Interface::Position.new(line: range.line - 1, character: range.column),
|
78
|
+
end: LanguageServer::Protocol::Interface::Position.new(line: range.last_line - 1,
|
79
|
+
character: range.last_column)
|
80
|
+
),
|
81
|
+
new_text: replacement
|
82
|
+
)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
module Support
|
7
|
+
class SelectionRange < LanguageServer::Protocol::Interface::SelectionRange
|
8
|
+
def cover?(position)
|
9
|
+
line_range = (range.start.line..range.end.line)
|
10
|
+
character_range = (range.start.character..range.end.character)
|
11
|
+
|
12
|
+
line_range.cover?(position[:line]) && character_range.cover?(position[:character])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
module Support
|
7
|
+
class SemanticTokenEncoder
|
8
|
+
def initialize
|
9
|
+
@current_row = 0
|
10
|
+
@current_column = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def encode(tokens)
|
14
|
+
delta = tokens
|
15
|
+
.sort_by do |token|
|
16
|
+
[token.location.start_line, token.location.start_column]
|
17
|
+
end
|
18
|
+
.flat_map do |token|
|
19
|
+
compute_delta(token)
|
20
|
+
end
|
21
|
+
|
22
|
+
LanguageServer::Protocol::Interface::SemanticTokens.new(data: delta)
|
23
|
+
end
|
24
|
+
|
25
|
+
# The delta array is computed according to the LSP specification:
|
26
|
+
# > The protocol for the token format relative uses relative
|
27
|
+
# > positions, because most tokens remain stable relative to
|
28
|
+
# > each other when edits are made in a file. This simplifies
|
29
|
+
# > the computation of a delta if a server supports it. So each
|
30
|
+
# > token is represented using 5 integers.
|
31
|
+
|
32
|
+
# For more information on how each number is calculated, read:
|
33
|
+
# https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens
|
34
|
+
def compute_delta(token)
|
35
|
+
row = token.location.start_line - 1
|
36
|
+
column = token.location.start_column
|
37
|
+
delta_line = row - @current_row
|
38
|
+
|
39
|
+
delta_column = column
|
40
|
+
delta_column -= @current_column if delta_line == 0
|
41
|
+
|
42
|
+
[delta_line, delta_column, token.length, token.type, encode_modifiers(token.modifier)]
|
43
|
+
ensure
|
44
|
+
@current_row = row
|
45
|
+
@current_column = column
|
46
|
+
end
|
47
|
+
|
48
|
+
# Encode an array of modifiers to positions onto a bit flag
|
49
|
+
# For example, [:default_library] will be encoded as
|
50
|
+
# 0b1000000000, as :default_library is the 10th bit according
|
51
|
+
# to the token modifiers index map.
|
52
|
+
def encode_modifiers(modifiers)
|
53
|
+
modifiers.inject(0) do |encoded_modifiers, modifier|
|
54
|
+
encoded_modifiers | (1 << modifier)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
module Support
|
7
|
+
class SyntaxErrorDiagnostic
|
8
|
+
def initialize(edit)
|
9
|
+
@edit = edit
|
10
|
+
end
|
11
|
+
|
12
|
+
def correctable?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_lsp_diagnostic
|
17
|
+
LanguageServer::Protocol::Interface::Diagnostic.new(
|
18
|
+
message: "Syntax error",
|
19
|
+
source: "SyntaxTree",
|
20
|
+
severity: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
|
21
|
+
range: @edit[:range]
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
autoload :BaseRequest, "ruby_lsp/requests/base_request"
|
7
|
+
autoload :DocumentSymbol, "ruby_lsp/requests/document_symbol"
|
8
|
+
autoload :FoldingRanges, "ruby_lsp/requests/folding_ranges"
|
9
|
+
autoload :SelectionRanges, "ruby_lsp/requests/selection_ranges"
|
10
|
+
autoload :SemanticHighlighting, "ruby_lsp/requests/semantic_highlighting"
|
11
|
+
autoload :RuboCopRequest, "ruby_lsp/requests/rubocop_request"
|
12
|
+
autoload :Formatting, "ruby_lsp/requests/formatting"
|
13
|
+
autoload :Diagnostics, "ruby_lsp/requests/diagnostics"
|
14
|
+
autoload :CodeActions, "ruby_lsp/requests/code_actions"
|
15
|
+
autoload :DocumentHighlight, "ruby_lsp/requests/document_highlight"
|
16
|
+
|
17
|
+
module Support
|
18
|
+
autoload :RuboCopDiagnostic, "ruby_lsp/requests/support/rubocop_diagnostic"
|
19
|
+
autoload :SelectionRange, "ruby_lsp/requests/support/selection_range"
|
20
|
+
autoload :SemanticTokenEncoder, "ruby_lsp/requests/support/semantic_token_encoder"
|
21
|
+
autoload :SyntaxErrorDiagnostic, "ruby_lsp/requests/support/syntax_error_diagnostic"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "cgi"
|
5
|
+
require "uri"
|
6
|
+
require "ruby_lsp/document"
|
7
|
+
|
8
|
+
module RubyLsp
|
9
|
+
class Store
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { void }
|
13
|
+
def initialize
|
14
|
+
@state = T.let({}, T::Hash[String, Document])
|
15
|
+
end
|
16
|
+
|
17
|
+
sig { params(uri: String).returns(Document) }
|
18
|
+
def get(uri)
|
19
|
+
document = @state[uri]
|
20
|
+
return document unless document.nil?
|
21
|
+
|
22
|
+
set(uri, File.binread(CGI.unescape(URI.parse(uri).path)))
|
23
|
+
T.must(@state[uri])
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { params(uri: String, content: String).void }
|
27
|
+
def set(uri, content)
|
28
|
+
@state[uri] = Document.new(content)
|
29
|
+
rescue SyntaxTree::Parser::ParseError
|
30
|
+
# Do not update the store if there are syntax errors
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { params(uri: String, edits: T::Array[Document::EditShape]).void }
|
34
|
+
def push_edits(uri, edits)
|
35
|
+
T.must(@state[uri]).push_edits(edits)
|
36
|
+
end
|
37
|
+
|
38
|
+
sig { void }
|
39
|
+
def clear
|
40
|
+
@state.clear
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { params(uri: String).void }
|
44
|
+
def delete(uri)
|
45
|
+
@state.delete(uri)
|
46
|
+
end
|
47
|
+
|
48
|
+
sig do
|
49
|
+
params(
|
50
|
+
uri: String,
|
51
|
+
request_name: Symbol,
|
52
|
+
block: T.proc.params(document: Document).returns(T.untyped)
|
53
|
+
).returns(T.untyped)
|
54
|
+
end
|
55
|
+
def cache_fetch(uri, request_name, &block)
|
56
|
+
get(uri).cache_fetch(request_name, &block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
desc "Check if all LSP requests are documented"
|
5
|
+
task :check_docs do
|
6
|
+
require "language_server-protocol"
|
7
|
+
require "syntax_tree"
|
8
|
+
require "logger"
|
9
|
+
require "ruby_lsp/requests/base_request"
|
10
|
+
require "ruby_lsp/requests/rubocop_request"
|
11
|
+
|
12
|
+
Dir["#{Dir.pwd}/lib/ruby_lsp/requests/*.rb"].each do |file|
|
13
|
+
require(file)
|
14
|
+
YARD.parse(file, [], Logger::Severity::FATAL)
|
15
|
+
end
|
16
|
+
|
17
|
+
spec_matcher = %r{\(https://microsoft.github.io/language-server-protocol/specification#.*\)}
|
18
|
+
error_messages = RubyLsp::Requests
|
19
|
+
.constants # rubocop:disable Sorbet/ConstantsFromStrings
|
20
|
+
.each_with_object(Hash.new { |h, k| h[k] = [] }) do |request, errors|
|
21
|
+
full_name = "RubyLsp::Requests::#{request}"
|
22
|
+
docs = YARD::Registry.at(full_name).docstring
|
23
|
+
next if /:nodoc:/.match?(docs)
|
24
|
+
|
25
|
+
if docs.empty?
|
26
|
+
errors[full_name] << "Missing documentation for request handler class"
|
27
|
+
elsif !spec_matcher.match?(docs)
|
28
|
+
errors[full_name] << <<~MESSAGE
|
29
|
+
Documentation for request handler classes must link to the official LSP specification.
|
30
|
+
|
31
|
+
For example, if your request handles text document hover, you should add a link to
|
32
|
+
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover.
|
33
|
+
MESSAGE
|
34
|
+
elsif !/# Example/.match?(docs)
|
35
|
+
errors[full_name] << <<~MESSAGE
|
36
|
+
Documentation for request handler class must contain an example.
|
37
|
+
|
38
|
+
= Example
|
39
|
+
def my_method # <-- something happens here
|
40
|
+
end
|
41
|
+
MESSAGE
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
formatted_errors = error_messages.map { |name, errors| "#{name}: #{errors.join(", ")}" }
|
46
|
+
|
47
|
+
if error_messages.any?
|
48
|
+
puts <<~MESSAGE
|
49
|
+
The following requests have invalid documentation:
|
50
|
+
|
51
|
+
#{formatted_errors.join("\n")}
|
52
|
+
MESSAGE
|
53
|
+
|
54
|
+
exit!
|
55
|
+
end
|
56
|
+
end
|
data/ruby-lsp.gemspec
CHANGED
@@ -10,6 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.summary = "A simple language server for ruby"
|
11
11
|
s.description = "A simple language server for ruby"
|
12
12
|
s.homepage = "https://github.com/Shopify/ruby-lsp"
|
13
|
+
s.license = "MIT"
|
13
14
|
|
14
15
|
s.files = Dir.chdir(File.expand_path(__dir__)) do
|
15
16
|
%x(git ls-files -z).split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
@@ -18,5 +19,8 @@ Gem::Specification.new do |s|
|
|
18
19
|
s.executables = s.files.grep(/\Aexe/) { |f| File.basename(f) }
|
19
20
|
s.require_paths = ["lib"]
|
20
21
|
|
21
|
-
s.add_dependency
|
22
|
+
s.add_dependency("language_server-protocol")
|
23
|
+
s.add_dependency("rubocop", ">= 1.0")
|
24
|
+
s.add_dependency("sorbet-runtime")
|
25
|
+
s.add_dependency("syntax_tree", ">= 2.3")
|
22
26
|
end
|
File without changes
|
data/sorbet/config
ADDED