steep 0.43.1 → 0.46.0
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 +8 -0
- data/.github/workflows/ruby.yml +4 -2
- data/.gitignore +0 -1
- data/CHANGELOG.md +41 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +77 -0
- data/bin/output_test.rb +8 -2
- data/lib/steep/ast/builtin.rb +7 -1
- data/lib/steep/ast/types/factory.rb +19 -25
- data/lib/steep/cli.rb +7 -1
- data/lib/steep/diagnostic/lsp_formatter.rb +59 -6
- data/lib/steep/diagnostic/ruby.rb +188 -60
- data/lib/steep/diagnostic/signature.rb +34 -0
- data/lib/steep/drivers/check.rb +3 -0
- data/lib/steep/drivers/init.rb +10 -3
- data/lib/steep/drivers/utils/driver_helper.rb +15 -0
- data/lib/steep/drivers/validate.rb +1 -1
- data/lib/steep/drivers/watch.rb +3 -0
- data/lib/steep/equatable.rb +21 -0
- data/lib/steep/index/source_index.rb +55 -5
- data/lib/steep/interface/block.rb +4 -0
- data/lib/steep/interface/function.rb +798 -579
- data/lib/steep/project/dsl.rb +105 -33
- data/lib/steep/project/options.rb +12 -53
- data/lib/steep/project/target.rb +21 -8
- data/lib/steep/server/interaction_worker.rb +239 -20
- data/lib/steep/server/master.rb +22 -1
- data/lib/steep/server/type_check_worker.rb +74 -9
- data/lib/steep/services/file_loader.rb +26 -19
- data/lib/steep/services/goto_service.rb +322 -0
- data/lib/steep/services/hover_content.rb +132 -80
- data/lib/steep/services/type_check_service.rb +25 -0
- data/lib/steep/source.rb +7 -10
- data/lib/steep/type_construction.rb +496 -518
- data/lib/steep/type_inference/block_params.rb +2 -5
- data/lib/steep/type_inference/method_params.rb +483 -0
- data/lib/steep/type_inference/send_args.rb +610 -128
- data/lib/steep/typing.rb +46 -21
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +4 -1
- data/sample/Steepfile +10 -3
- data/sig/steep/type_inference/send_args.rbs +42 -0
- data/smoke/alias/Steepfile +2 -1
- data/smoke/and/Steepfile +2 -1
- data/smoke/array/Steepfile +2 -1
- data/smoke/array/test_expectations.yml +3 -3
- data/smoke/block/Steepfile +2 -2
- data/smoke/block/c.rb +0 -1
- data/smoke/case/Steepfile +2 -1
- data/smoke/class/Steepfile +2 -1
- data/smoke/class/test_expectations.yml +12 -15
- data/smoke/const/Steepfile +2 -1
- data/smoke/const/test_expectations.yml +0 -10
- data/smoke/diagnostics/Steepfile +2 -1
- data/smoke/diagnostics/a.rbs +0 -4
- data/smoke/diagnostics/different_method_parameter_kind.rb +9 -0
- data/smoke/diagnostics/method_arity_mismatch.rb +2 -2
- data/smoke/diagnostics/method_parameter_mismatch.rb +10 -0
- data/smoke/diagnostics/test_expectations.yml +108 -57
- data/smoke/diagnostics-rbs/Steepfile +1 -1
- data/smoke/diagnostics-rbs/mixin-class-error.rbs +6 -0
- data/smoke/diagnostics-rbs/test_expectations.yml +12 -0
- data/smoke/diagnostics-rbs-duplicated/Steepfile +2 -1
- data/smoke/diagnostics-ruby-unsat/Steepfile +6 -0
- data/smoke/diagnostics-ruby-unsat/a.rbs +3 -0
- data/smoke/diagnostics-ruby-unsat/test_expectations.yml +27 -0
- data/smoke/{diagnostics → diagnostics-ruby-unsat}/unsatisfiable_constraint.rb +0 -1
- data/smoke/dstr/Steepfile +2 -1
- data/smoke/ensure/Steepfile +2 -1
- data/smoke/ensure/test_expectations.yml +3 -3
- data/smoke/enumerator/Steepfile +2 -1
- data/smoke/enumerator/test_expectations.yml +1 -1
- data/smoke/extension/Steepfile +2 -1
- data/smoke/hash/Steepfile +2 -1
- data/smoke/hello/Steepfile +2 -1
- data/smoke/if/Steepfile +2 -1
- data/smoke/implements/Steepfile +2 -1
- data/smoke/initialize/Steepfile +2 -1
- data/smoke/integer/Steepfile +2 -1
- data/smoke/interface/Steepfile +2 -1
- data/smoke/kwbegin/Steepfile +2 -1
- data/smoke/lambda/Steepfile +2 -1
- data/smoke/literal/Steepfile +2 -1
- data/smoke/literal/test_expectations.yml +2 -2
- data/smoke/map/Steepfile +2 -1
- data/smoke/method/Steepfile +2 -1
- data/smoke/method/test_expectations.yml +11 -10
- data/smoke/module/Steepfile +2 -1
- data/smoke/regexp/Steepfile +2 -1
- data/smoke/regression/Steepfile +2 -1
- data/smoke/regression/issue_372.rb +8 -0
- data/smoke/regression/issue_372.rbs +4 -0
- data/smoke/regression/test_expectations.yml +0 -12
- data/smoke/rescue/Steepfile +2 -1
- data/smoke/rescue/test_expectations.yml +3 -3
- data/smoke/self/Steepfile +2 -1
- data/smoke/skip/Steepfile +2 -1
- data/smoke/stdout/Steepfile +2 -1
- data/smoke/super/Steepfile +2 -1
- data/smoke/toplevel/Steepfile +2 -1
- data/smoke/toplevel/test_expectations.yml +3 -3
- data/smoke/tsort/Steepfile +4 -5
- data/smoke/tsort/test_expectations.yml +2 -2
- data/smoke/type_case/Steepfile +2 -1
- data/smoke/unexpected/Steepfile +2 -1
- data/smoke/yield/Steepfile +2 -1
- data/steep.gemspec +2 -2
- metadata +24 -10
data/lib/steep/project/dsl.rb
CHANGED
@@ -7,21 +7,21 @@ module Steep
|
|
7
7
|
attr_reader :libraries
|
8
8
|
attr_reader :signatures
|
9
9
|
attr_reader :ignored_sources
|
10
|
-
attr_reader :
|
11
|
-
attr_reader :
|
12
|
-
attr_reader :typing_option_hash
|
10
|
+
attr_reader :stdlib_root
|
11
|
+
attr_reader :core_root
|
13
12
|
attr_reader :repo_paths
|
13
|
+
attr_reader :code_diagnostics_config
|
14
14
|
|
15
|
-
def initialize(name, sources: [], libraries: [], signatures: [], ignored_sources: [], repo_paths: [])
|
15
|
+
def initialize(name, sources: [], libraries: [], signatures: [], ignored_sources: [], repo_paths: [], code_diagnostics_config: {})
|
16
16
|
@name = name
|
17
17
|
@sources = sources
|
18
18
|
@libraries = libraries
|
19
19
|
@signatures = signatures
|
20
20
|
@ignored_sources = ignored_sources
|
21
|
-
@
|
22
|
-
@
|
23
|
-
@typing_option_hash = {}
|
21
|
+
@core_root = nil
|
22
|
+
@stdlib_root = nil
|
24
23
|
@repo_paths = []
|
24
|
+
@code_diagnostics_config = code_diagnostics_config
|
25
25
|
end
|
26
26
|
|
27
27
|
def initialize_copy(other)
|
@@ -30,10 +30,10 @@ module Steep
|
|
30
30
|
@libraries = other.libraries.dup
|
31
31
|
@signatures = other.signatures.dup
|
32
32
|
@ignored_sources = other.ignored_sources.dup
|
33
|
-
@vendor_dir = other.vendor_dir
|
34
|
-
@strictness_level = other.strictness_level
|
35
|
-
@typing_option_hash = other.typing_option_hash
|
36
33
|
@repo_paths = other.repo_paths.dup
|
34
|
+
@core_root = other.core_root
|
35
|
+
@stdlib_root = other.stdlib_root
|
36
|
+
@code_diagnostics_config = other.code_diagnostics_config.dup
|
37
37
|
end
|
38
38
|
|
39
39
|
def check(*args)
|
@@ -48,9 +48,52 @@ module Steep
|
|
48
48
|
libraries.push(*args)
|
49
49
|
end
|
50
50
|
|
51
|
-
def typing_options(level =
|
52
|
-
|
53
|
-
|
51
|
+
def typing_options(level = nil, **hash)
|
52
|
+
Steep.logger.error "#typing_options is deprecated and has no effect as of version 0.46.0. Update your Steepfile as follows for (almost) equivalent setting:"
|
53
|
+
|
54
|
+
messages = []
|
55
|
+
|
56
|
+
messages << "# D = Steep::Diagnostic # Define a constant to shorten namespace"
|
57
|
+
|
58
|
+
case level
|
59
|
+
when :strict
|
60
|
+
messages << "configure_code_diagnostics(D::Ruby.strict) # :strict"
|
61
|
+
when :default
|
62
|
+
messages << "configure_code_diagnostics(D::Ruby.default) # :default"
|
63
|
+
when :lenient
|
64
|
+
messages << "configure_code_diagnostics(D::Ruby.lenient) # :lenient"
|
65
|
+
end
|
66
|
+
|
67
|
+
messages.each do |msg|
|
68
|
+
Steep.logger.error " #{msg}"
|
69
|
+
end
|
70
|
+
|
71
|
+
config = []
|
72
|
+
|
73
|
+
if hash[:allow_missing_definitions]
|
74
|
+
config << "hash[D::Ruby::MethodDefinitionMissing] = nil # allow_missing_definitions"
|
75
|
+
end
|
76
|
+
|
77
|
+
if hash[:allow_fallback_any]
|
78
|
+
config << "hash[D::Ruby::FallbackAny] = nil # allow_fallback_any"
|
79
|
+
end
|
80
|
+
|
81
|
+
if hash[:allow_unknown_constant_assignment]
|
82
|
+
config << "hash[D::Ruby::UnknownConstantAssigned] = nil # allow_unknown_constant_assignment"
|
83
|
+
end
|
84
|
+
|
85
|
+
if hash[:allow_unknown_method_calls]
|
86
|
+
config << "hash[D::Ruby::NoMethod] = nil # allow_unknown_method_calls"
|
87
|
+
end
|
88
|
+
|
89
|
+
unless config.empty?
|
90
|
+
Steep.logger.error " configure_code_diagnostics do |hash|"
|
91
|
+
config.each do |c|
|
92
|
+
Steep.logger.error " #{c}"
|
93
|
+
end
|
94
|
+
Steep.logger.error " end"
|
95
|
+
end
|
96
|
+
|
54
97
|
end
|
55
98
|
|
56
99
|
def signature(*args)
|
@@ -68,20 +111,50 @@ module Steep
|
|
68
111
|
end
|
69
112
|
|
70
113
|
def no_builtin!(value = true)
|
71
|
-
Steep.logger.error "
|
114
|
+
Steep.logger.error "`#no_builtin!` in Steepfile is deprecated and ignored. Use `#stdlib_path` instead."
|
72
115
|
end
|
73
116
|
|
74
117
|
def vendor(dir = "vendor/sigs", stdlib: nil, gems: nil)
|
75
|
-
|
76
|
-
|
77
|
-
end
|
118
|
+
Steep.logger.error "`#vendor` in Steepfile is deprecated and ignored. Use `#stdlib_path` instead."
|
119
|
+
end
|
78
120
|
|
79
|
-
|
121
|
+
def stdlib_path(core_root:, stdlib_root:)
|
122
|
+
@core_root = core_root ? Pathname(core_root) : core_root
|
123
|
+
@stdlib_root = stdlib_root ? Pathname(stdlib_root) : stdlib_root
|
80
124
|
end
|
81
125
|
|
82
126
|
def repo_path(*paths)
|
83
127
|
@repo_paths.push(*paths.map {|s| Pathname(s) })
|
84
128
|
end
|
129
|
+
|
130
|
+
# Configure the code diagnostics printing setup.
|
131
|
+
#
|
132
|
+
# Yields a hash, and the update the hash in the block.
|
133
|
+
#
|
134
|
+
# ```rb
|
135
|
+
# D = Steep::Diagnostic
|
136
|
+
#
|
137
|
+
# configure_code_diagnostics do |hash|
|
138
|
+
# # Assign one of :error, :warning, :information, :hint or :nil to error classes.
|
139
|
+
# hash[D::Ruby::UnexpectedPositionalArgument] = :error
|
140
|
+
# end
|
141
|
+
# ```
|
142
|
+
#
|
143
|
+
# Passing a hash is also allowed.
|
144
|
+
#
|
145
|
+
# ```rb
|
146
|
+
# D = Steep::Diagnostic
|
147
|
+
#
|
148
|
+
# configure_code_diagnostics(D::Ruby.lenient)
|
149
|
+
# ```
|
150
|
+
#
|
151
|
+
def configure_code_diagnostics(hash = nil)
|
152
|
+
if hash
|
153
|
+
code_diagnostics_config.merge!(hash)
|
154
|
+
end
|
155
|
+
|
156
|
+
yield code_diagnostics_config if block_given?
|
157
|
+
end
|
85
158
|
end
|
86
159
|
|
87
160
|
attr_reader :project
|
@@ -107,7 +180,9 @@ module Steep
|
|
107
180
|
end
|
108
181
|
|
109
182
|
def self.parse(project, code, filename: "Steepfile")
|
110
|
-
|
183
|
+
Steep.logger.tagged filename do
|
184
|
+
self.new(project: project).instance_eval(code, filename)
|
185
|
+
end
|
111
186
|
end
|
112
187
|
|
113
188
|
def target(name, template: nil, &block)
|
@@ -115,10 +190,12 @@ module Steep
|
|
115
190
|
self.class.templates[template]&.dup&.update(name: name) or
|
116
191
|
raise "Unknown template: #{template}, available templates: #{@@templates.keys.join(", ")}"
|
117
192
|
else
|
118
|
-
TargetDSL.new(name)
|
193
|
+
TargetDSL.new(name, code_diagnostics_config: Diagnostic::Ruby.default.dup)
|
119
194
|
end
|
120
195
|
|
121
|
-
|
196
|
+
Steep.logger.tagged "target=#{name}" do
|
197
|
+
target.instance_eval(&block) if block_given?
|
198
|
+
end
|
122
199
|
|
123
200
|
source_pattern = Pattern.new(patterns: target.sources, ignores: target.ignored_sources, ext: ".rb")
|
124
201
|
signature_pattern = Pattern.new(patterns: target.signatures, ext: ".rbs")
|
@@ -129,18 +206,13 @@ module Steep
|
|
129
206
|
signature_pattern: signature_pattern,
|
130
207
|
options: Options.new.tap do |options|
|
131
208
|
options.libraries.push(*target.libraries)
|
132
|
-
options.
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
options.apply_lenient_typing_options!
|
140
|
-
end
|
141
|
-
|
142
|
-
options.merge!(target.typing_option_hash)
|
143
|
-
end
|
209
|
+
options.paths = Options::PathOptions.new(
|
210
|
+
core_root: target.core_root,
|
211
|
+
stdlib_root: target.stdlib_root,
|
212
|
+
repo_paths: target.repo_paths
|
213
|
+
)
|
214
|
+
end,
|
215
|
+
code_diagnostics_config: target.code_diagnostics_config
|
144
216
|
).tap do |target|
|
145
217
|
project.targets << target
|
146
218
|
end
|
@@ -1,63 +1,22 @@
|
|
1
1
|
module Steep
|
2
2
|
class Project
|
3
3
|
class Options
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
attr_accessor :vendor_path
|
9
|
-
attr_reader :libraries
|
10
|
-
attr_reader :repository_paths
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
apply_default_typing_options!
|
14
|
-
self.vendor_path = nil
|
15
|
-
|
16
|
-
@libraries = []
|
17
|
-
@repository_paths = []
|
18
|
-
end
|
19
|
-
|
20
|
-
def apply_default_typing_options!
|
21
|
-
self.allow_fallback_any = true
|
22
|
-
self.allow_missing_definitions = true
|
23
|
-
self.allow_unknown_constant_assignment = false
|
24
|
-
self.allow_unknown_method_calls = false
|
25
|
-
end
|
26
|
-
|
27
|
-
def apply_strict_typing_options!
|
28
|
-
self.allow_fallback_any = false
|
29
|
-
self.allow_missing_definitions = false
|
30
|
-
self.allow_unknown_constant_assignment = false
|
31
|
-
self.allow_unknown_method_calls = false
|
32
|
-
end
|
33
|
-
|
34
|
-
def apply_lenient_typing_options!
|
35
|
-
self.allow_fallback_any = true
|
36
|
-
self.allow_missing_definitions = true
|
37
|
-
self.allow_unknown_constant_assignment = true
|
38
|
-
self.allow_unknown_method_calls = true
|
39
|
-
end
|
4
|
+
PathOptions = Struct.new(:core_root, :stdlib_root, :repo_paths, keyword_init: true) do
|
5
|
+
def customized_stdlib?
|
6
|
+
stdlib_root != nil
|
7
|
+
end
|
40
8
|
|
41
|
-
|
42
|
-
|
43
|
-
when error.is_a?(Diagnostic::Ruby::FallbackAny)
|
44
|
-
!allow_fallback_any
|
45
|
-
when error.is_a?(Diagnostic::Ruby::MethodDefinitionMissing)
|
46
|
-
!allow_missing_definitions
|
47
|
-
when error.is_a?(Diagnostic::Ruby::NoMethod)
|
48
|
-
!allow_unknown_method_calls
|
49
|
-
when error.is_a?(Diagnostic::Ruby::UnknownConstantAssigned)
|
50
|
-
!allow_unknown_constant_assignment
|
51
|
-
else
|
52
|
-
true
|
9
|
+
def customized_core?
|
10
|
+
core_root != nil
|
53
11
|
end
|
54
12
|
end
|
55
13
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
14
|
+
attr_reader :libraries
|
15
|
+
attr_accessor :paths
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@paths = PathOptions.new(repo_paths: [])
|
19
|
+
@libraries = []
|
61
20
|
end
|
62
21
|
end
|
63
22
|
end
|
data/lib/steep/project/target.rb
CHANGED
@@ -6,12 +6,14 @@ module Steep
|
|
6
6
|
|
7
7
|
attr_reader :source_pattern
|
8
8
|
attr_reader :signature_pattern
|
9
|
+
attr_reader :code_diagnostics_config
|
9
10
|
|
10
|
-
def initialize(name:, options:, source_pattern:, signature_pattern:)
|
11
|
+
def initialize(name:, options:, source_pattern:, signature_pattern:, code_diagnostics_config:)
|
11
12
|
@name = name
|
12
13
|
@options = options
|
13
14
|
@source_pattern = source_pattern
|
14
15
|
@signature_pattern = signature_pattern
|
16
|
+
@code_diagnostics_config = code_diagnostics_config
|
15
17
|
|
16
18
|
@source_files = {}
|
17
19
|
@signature_files = {}
|
@@ -30,16 +32,27 @@ module Steep
|
|
30
32
|
end
|
31
33
|
|
32
34
|
def self.construct_env_loader(options:, project:)
|
33
|
-
repo = RBS::Repository.new(no_stdlib: options.
|
34
|
-
|
35
|
+
repo = RBS::Repository.new(no_stdlib: options.paths.customized_stdlib?)
|
36
|
+
|
37
|
+
if options.paths.stdlib_root
|
38
|
+
repo.add(project.absolute_path(options.paths.stdlib_root))
|
39
|
+
end
|
40
|
+
|
41
|
+
options.paths.repo_paths.each do |path|
|
35
42
|
repo.add(project.absolute_path(path))
|
36
43
|
end
|
37
44
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
45
|
+
core_root_path =
|
46
|
+
if options.paths.customized_core?
|
47
|
+
if options.paths.core_root
|
48
|
+
project.absolute_path(options.paths.core_root)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
RBS::EnvironmentLoader::DEFAULT_CORE_ROOT
|
52
|
+
end
|
53
|
+
|
54
|
+
loader = RBS::EnvironmentLoader.new(core_root: core_root_path, repository: repo)
|
55
|
+
|
43
56
|
options.libraries.each do |lib|
|
44
57
|
name, version = lib.split(/:/, 2)
|
45
58
|
loader.add(library: name, version: version)
|
@@ -7,6 +7,8 @@ module Steep
|
|
7
7
|
HoverJob = Struct.new(:id, :path, :line, :column, keyword_init: true)
|
8
8
|
CompletionJob = Struct.new(:id, :path, :line, :column, :trigger, keyword_init: true)
|
9
9
|
|
10
|
+
LSP = LanguageServer::Protocol
|
11
|
+
|
10
12
|
attr_reader :service
|
11
13
|
|
12
14
|
def initialize(project:, reader:, writer:, queue: Queue.new)
|
@@ -65,7 +67,7 @@ module Steep
|
|
65
67
|
uri = URI.parse(params[:textDocument][:uri])
|
66
68
|
path = project.relative_path(Pathname(uri.path))
|
67
69
|
line, column = params[:position].yield_self {|hash| [hash[:line]+1, hash[:character]] }
|
68
|
-
trigger = params
|
70
|
+
trigger = params.dig(:context, :triggerCharacter)
|
69
71
|
|
70
72
|
queue << CompletionJob.new(id: id, path: path, line: line, column: column, trigger: trigger)
|
71
73
|
end
|
@@ -77,11 +79,12 @@ module Steep
|
|
77
79
|
Steep.logger.info { "path=#{job.path}, line=#{job.line}, column=#{job.column}" }
|
78
80
|
|
79
81
|
hover = Services::HoverContent.new(service: service)
|
80
|
-
content = hover.content_for(path: job.path, line: job.line, column: job.column
|
82
|
+
content = hover.content_for(path: job.path, line: job.line, column: job.column)
|
81
83
|
if content
|
82
84
|
range = content.location.yield_self do |location|
|
83
|
-
|
84
|
-
|
85
|
+
lsp_range = location.as_lsp_range
|
86
|
+
start_position = { line: lsp_range[:start][:line], character: lsp_range[:start][:character] }
|
87
|
+
end_position = { line: lsp_range[:end][:line], character: lsp_range[:end][:character] }
|
85
88
|
{ start: start_position, end: end_position }
|
86
89
|
end
|
87
90
|
|
@@ -99,6 +102,36 @@ module Steep
|
|
99
102
|
|
100
103
|
def format_hover(content)
|
101
104
|
case content
|
105
|
+
when Services::HoverContent::TypeAliasContent
|
106
|
+
comment = content.decl.comment&.string || ''
|
107
|
+
|
108
|
+
<<-MD
|
109
|
+
#{comment}
|
110
|
+
|
111
|
+
```rbs
|
112
|
+
#{retrieve_decl_information(content.decl)}
|
113
|
+
```
|
114
|
+
MD
|
115
|
+
when Services::HoverContent::InterfaceContent
|
116
|
+
comment = content.decl.comment&.string || ''
|
117
|
+
|
118
|
+
<<-MD
|
119
|
+
#{comment}
|
120
|
+
|
121
|
+
```rbs
|
122
|
+
#{retrieve_decl_information(content.decl)}
|
123
|
+
```
|
124
|
+
MD
|
125
|
+
when Services::HoverContent::ClassContent
|
126
|
+
comment = content.decl.comment&.string || ''
|
127
|
+
|
128
|
+
<<-MD
|
129
|
+
#{comment}
|
130
|
+
|
131
|
+
```rbs
|
132
|
+
#{retrieve_decl_information(content.decl)}
|
133
|
+
```
|
134
|
+
MD
|
102
135
|
when Services::HoverContent::VariableContent
|
103
136
|
"`#{content.name}`: `#{content.type.to_s}`"
|
104
137
|
when Services::HoverContent::MethodCallContent
|
@@ -151,32 +184,207 @@ HOVER
|
|
151
184
|
Steep.logger.tagged("#response_to_completion") do
|
152
185
|
Steep.measure "Generating response" do
|
153
186
|
Steep.logger.info "path: #{job.path}, line: #{job.line}, column: #{job.column}, trigger: #{job.trigger}"
|
187
|
+
case
|
188
|
+
when target = project.target_for_source_path(job.path)
|
189
|
+
file = service.source_files[job.path] or return
|
190
|
+
subtyping = service.signature_services[target.name].current_subtyping or return
|
191
|
+
|
192
|
+
provider = Services::CompletionProvider.new(source_text: file.content, path: job.path, subtyping: subtyping)
|
193
|
+
items = begin
|
194
|
+
provider.run(line: job.line, column: job.column)
|
195
|
+
rescue Parser::SyntaxError
|
196
|
+
[]
|
197
|
+
end
|
198
|
+
|
199
|
+
completion_items = items.map do |item|
|
200
|
+
format_completion_item(item)
|
201
|
+
end
|
202
|
+
|
203
|
+
Steep.logger.debug "items = #{completion_items.inspect}"
|
204
|
+
|
205
|
+
LSP::Interface::CompletionList.new(
|
206
|
+
is_incomplete: false,
|
207
|
+
items: completion_items
|
208
|
+
)
|
209
|
+
when (_, targets = project.targets_for_path(job.path))
|
210
|
+
target = targets[0] or return
|
211
|
+
sig_service = service.signature_services[target.name]
|
212
|
+
relative_path = job.path
|
213
|
+
buffer = RBS::Buffer.new(name: relative_path, content: sig_service.files[relative_path].content)
|
214
|
+
pos = buffer.loc_to_pos([job.line, job.column])
|
215
|
+
prefix = buffer.content[0...pos].reverse[/\A[\w\d]*/].reverse
|
216
|
+
|
217
|
+
case sig_service.status
|
218
|
+
when Steep::Services::SignatureService::SyntaxErrorStatus, Steep::Services::SignatureService::AncestorErrorStatus
|
219
|
+
return
|
220
|
+
end
|
221
|
+
|
222
|
+
decls = sig_service.files[relative_path].decls
|
223
|
+
locator = RBS::Locator.new(decls: decls)
|
224
|
+
|
225
|
+
hd, tail = locator.find2(line: job.line, column: job.column)
|
226
|
+
|
227
|
+
namespace = []
|
228
|
+
tail.each do |t|
|
229
|
+
case t
|
230
|
+
when RBS::AST::Declarations::Module, RBS::AST::Declarations::Class
|
231
|
+
namespace << t.name.to_namespace
|
232
|
+
end
|
233
|
+
end
|
234
|
+
context = []
|
154
235
|
|
155
|
-
|
156
|
-
|
157
|
-
|
236
|
+
namespace.each do |ns|
|
237
|
+
context.map! { |n| ns + n }
|
238
|
+
context << ns
|
239
|
+
end
|
240
|
+
|
241
|
+
context.map!(&:absolute!)
|
242
|
+
|
243
|
+
class_items = sig_service.latest_env.class_decls.keys.map { |type_name|
|
244
|
+
format_completion_item_for_rbs(sig_service, type_name, context, job, prefix)
|
245
|
+
}.compact
|
246
|
+
|
247
|
+
alias_items = sig_service.latest_env.alias_decls.keys.map { |type_name|
|
248
|
+
format_completion_item_for_rbs(sig_service, type_name, context, job, prefix)
|
249
|
+
}.compact
|
250
|
+
|
251
|
+
interface_items = sig_service.latest_env.interface_decls.keys.map {|type_name|
|
252
|
+
format_completion_item_for_rbs(sig_service, type_name, context, job, prefix)
|
253
|
+
}.compact
|
158
254
|
|
159
|
-
|
160
|
-
items = begin
|
161
|
-
provider.run(line: job.line, column: job.column)
|
162
|
-
rescue Parser::SyntaxError
|
163
|
-
[]
|
164
|
-
end
|
255
|
+
completion_items = class_items + alias_items + interface_items
|
165
256
|
|
166
|
-
|
167
|
-
|
257
|
+
LSP::Interface::CompletionList.new(
|
258
|
+
is_incomplete: false,
|
259
|
+
items: completion_items
|
260
|
+
)
|
168
261
|
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def format_completion_item_for_rbs(sig_service, type_name, context, job, prefix)
|
267
|
+
range = LanguageServer::Protocol::Interface::Range.new(
|
268
|
+
start: LanguageServer::Protocol::Interface::Position.new(
|
269
|
+
line: job.line - 1,
|
270
|
+
character: job.column - prefix.size
|
271
|
+
),
|
272
|
+
end: LanguageServer::Protocol::Interface::Position.new(
|
273
|
+
line: job.line - 1,
|
274
|
+
character: job.column - prefix.size
|
275
|
+
)
|
276
|
+
)
|
169
277
|
|
170
|
-
|
278
|
+
name = relative_name_in_context(type_name, context).to_s
|
171
279
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
280
|
+
return unless name.start_with?(prefix)
|
281
|
+
|
282
|
+
case type_name.kind
|
283
|
+
when :class
|
284
|
+
class_decl = sig_service.latest_env.class_decls[type_name]&.decls[0]&.decl or raise
|
285
|
+
|
286
|
+
LanguageServer::Protocol::Interface::CompletionItem.new(
|
287
|
+
label: "#{name}",
|
288
|
+
documentation: format_comment(class_decl.comment),
|
289
|
+
text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
|
290
|
+
range: range,
|
291
|
+
new_text: name
|
292
|
+
),
|
293
|
+
kind: LSP::Constant::CompletionItemKind::CLASS,
|
294
|
+
insert_text_format: LSP::Constant::InsertTextFormat::SNIPPET
|
295
|
+
|
296
|
+
)
|
297
|
+
when :alias
|
298
|
+
alias_decl = sig_service.latest_env.alias_decls[type_name]&.decl or raise
|
299
|
+
LanguageServer::Protocol::Interface::CompletionItem.new(
|
300
|
+
label: "#{name}",
|
301
|
+
text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
|
302
|
+
range: range,
|
303
|
+
new_text: name
|
304
|
+
),
|
305
|
+
documentation: format_comment(alias_decl.comment),
|
306
|
+
# https://github.com/microsoft/vscode-languageserver-node/blob/6d78fc4d25719b231aba64a721a606f58b9e0a5f/client/src/common/client.ts#L624-L650
|
307
|
+
kind: LSP::Constant::CompletionItemKind::FIELD,
|
308
|
+
insert_text_format: LSP::Constant::InsertTextFormat::SNIPPET
|
309
|
+
)
|
310
|
+
when :interface
|
311
|
+
interface_decl = sig_service.latest_env.interface_decls[type_name]&.decl or raise
|
312
|
+
|
313
|
+
LanguageServer::Protocol::Interface::CompletionItem.new(
|
314
|
+
label: "#{name}",
|
315
|
+
text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
|
316
|
+
range: range,
|
317
|
+
new_text: name
|
318
|
+
),
|
319
|
+
documentation: format_comment(interface_decl.comment),
|
320
|
+
kind: LanguageServer::Protocol::Constant::CompletionItemKind::INTERFACE,
|
321
|
+
insert_text_format: LanguageServer::Protocol::Constant::InsertTextFormat::SNIPPET
|
322
|
+
)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def format_comment(comment)
|
327
|
+
if comment
|
328
|
+
LSP::Interface::MarkupContent.new(
|
329
|
+
kind: LSP::Constant::MarkupKind::MARKDOWN,
|
330
|
+
value: comment.string
|
331
|
+
)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def name_and_params(name, params)
|
336
|
+
if params.empty?
|
337
|
+
"#{name}"
|
338
|
+
else
|
339
|
+
ps = params.each.map do |param|
|
340
|
+
s = ""
|
341
|
+
if param.skip_validation
|
342
|
+
s << "unchecked "
|
343
|
+
end
|
344
|
+
case param.variance
|
345
|
+
when :invariant
|
346
|
+
# nop
|
347
|
+
when :covariant
|
348
|
+
s << "out "
|
349
|
+
when :contravariant
|
350
|
+
s << "in "
|
351
|
+
end
|
352
|
+
s + param.name.to_s
|
353
|
+
end
|
354
|
+
|
355
|
+
"#{name}[#{ps.join(", ")}]"
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def name_and_args(name, args)
|
360
|
+
if name && args
|
361
|
+
if args.empty?
|
362
|
+
"#{name}"
|
363
|
+
else
|
364
|
+
"#{name}[#{args.join(", ")}]"
|
176
365
|
end
|
177
366
|
end
|
178
367
|
end
|
179
368
|
|
369
|
+
def retrieve_decl_information(decl)
|
370
|
+
case decl
|
371
|
+
when RBS::AST::Declarations::Class
|
372
|
+
super_class = if super_class = decl.super_class
|
373
|
+
" < #{name_and_args(super_class.name, super_class.args)}"
|
374
|
+
end
|
375
|
+
"class #{name_and_params(decl.name, decl.type_params)}#{super_class}"
|
376
|
+
when RBS::AST::Declarations::Module
|
377
|
+
self_type = unless decl.self_types.empty?
|
378
|
+
" : #{decl.self_types.join(", ")}"
|
379
|
+
end
|
380
|
+
"module #{name_and_params(decl.name, decl.type_params)}#{self_type}"
|
381
|
+
when RBS::AST::Declarations::Alias
|
382
|
+
"type #{decl.name} = #{decl.type}"
|
383
|
+
when RBS::AST::Declarations::Interface
|
384
|
+
"interface #{name_and_params(decl.name, decl.type_params)}"
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
180
388
|
def format_completion_item(item)
|
181
389
|
range = LanguageServer::Protocol::Interface::Range.new(
|
182
390
|
start: LanguageServer::Protocol::Interface::Position.new(
|
@@ -297,6 +505,17 @@ HOVER
|
|
297
505
|
|
298
506
|
params.join(", ")
|
299
507
|
end
|
508
|
+
|
509
|
+
def relative_name_in_context(type_name, context)
|
510
|
+
context.each do |namespace|
|
511
|
+
if (type_name.to_s == namespace.to_type_name.to_s || type_name.namespace.to_s == "::")
|
512
|
+
return RBS::TypeName.new(namespace: RBS::Namespace.empty, name: type_name.name)
|
513
|
+
elsif type_name.to_s.start_with?(namespace.to_s)
|
514
|
+
return TypeName(type_name.to_s.sub(namespace.to_type_name.to_s, '')).relative!
|
515
|
+
end
|
516
|
+
end
|
517
|
+
type_name
|
518
|
+
end
|
300
519
|
end
|
301
520
|
end
|
302
521
|
end
|