steep 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitmodules +1 -1
- data/CHANGELOG.md +5 -0
- data/lib/steep/cli.rb +16 -1
- data/lib/steep/drivers/annotations.rb +1 -1
- data/lib/steep/drivers/langserver.rb +13 -462
- data/lib/steep/drivers/utils/driver_helper.rb +1 -1
- data/lib/steep/drivers/watch.rb +97 -85
- data/lib/steep/drivers/worker.rb +51 -0
- data/lib/steep/project/completion_provider.rb +4 -2
- data/lib/steep/project/file.rb +1 -0
- data/lib/steep/project/hover_content.rb +5 -2
- data/lib/steep/project/target.rb +30 -20
- data/lib/steep/project.rb +9 -5
- data/lib/steep/server/base_worker.rb +56 -0
- data/lib/steep/server/code_worker.rb +151 -0
- data/lib/steep/server/interaction_worker.rb +281 -0
- data/lib/steep/server/master.rb +196 -0
- data/lib/steep/server/signature_worker.rb +148 -0
- data/lib/steep/server/utils.rb +36 -0
- data/lib/steep/server/worker_process.rb +62 -0
- data/lib/steep/signature/validator.rb +5 -5
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +11 -0
- data/steep.gemspec +1 -1
- data/vendor/ruby-signature/README.md +18 -18
- data/vendor/ruby-signature/Rakefile +90 -15
- data/vendor/ruby-signature/rbs.gemspec +1 -0
- data/vendor/ruby-signature/stdlib/builtin/builtin.rbs +1 -0
- data/vendor/ruby-signature/stdlib/builtin/enumerable.rbs +1 -1
- data/vendor/ruby-signature/stdlib/builtin/module.rbs +2 -2
- data/vendor/ruby-signature/stdlib/json/json.rbs +335 -0
- metadata +14 -5
data/lib/steep/drivers/watch.rb
CHANGED
@@ -15,110 +15,122 @@ module Steep
|
|
15
15
|
@queue = Thread::Queue.new
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
Steep.logger.tagged "watch" do
|
23
|
-
Steep.logger.info "Received file system updates: modified=[#{modified.join(",")}], added=[#{added.join(",")}], removed=[#{removed.join(",")}]"
|
24
|
-
end
|
25
|
-
queue << [modified, added, removed]
|
26
|
-
end
|
18
|
+
def run()
|
19
|
+
if dirs.empty?
|
20
|
+
stdout.puts "Specify directories to watch"
|
21
|
+
return 1
|
27
22
|
end
|
28
|
-
end
|
29
23
|
|
30
|
-
|
31
|
-
until queue.closed?
|
32
|
-
stdout.puts "🚥 Waiting for updates..."
|
24
|
+
project = load_config()
|
33
25
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
events << queue.deq(nonblock: true)
|
38
|
-
end
|
26
|
+
loader = Project::FileLoader.new(project: project)
|
27
|
+
loader.load_sources([])
|
28
|
+
loader.load_signatures()
|
39
29
|
|
40
|
-
|
41
|
-
|
42
|
-
path = Pathname(name).relative_path_from(Pathname.pwd)
|
30
|
+
client_read, server_write = IO.pipe
|
31
|
+
server_read, client_write = IO.pipe
|
43
32
|
|
44
|
-
|
45
|
-
|
46
|
-
target.update_signature path, path.read if target.signature_file?(path)
|
47
|
-
end
|
48
|
-
end
|
33
|
+
client_reader = LanguageServer::Protocol::Transport::Io::Reader.new(client_read)
|
34
|
+
client_writer = LanguageServer::Protocol::Transport::Io::Writer.new(client_write)
|
49
35
|
|
50
|
-
|
51
|
-
|
36
|
+
server_reader = LanguageServer::Protocol::Transport::Io::Reader.new(server_read)
|
37
|
+
server_writer = LanguageServer::Protocol::Transport::Io::Writer.new(server_write)
|
52
38
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
end
|
39
|
+
interaction_worker = Server::InteractionWorker.spawn_worker(:interaction, name: "interaction", steepfile: project.steepfile_path)
|
40
|
+
signature_worker = Server::WorkerProcess.spawn_worker(:signature, name: "signature", steepfile: project.steepfile_path)
|
41
|
+
code_workers = Server::WorkerProcess.spawn_code_workers(steepfile: project.steepfile_path)
|
58
42
|
|
59
|
-
|
60
|
-
|
43
|
+
master = Server::Master.new(
|
44
|
+
project: project,
|
45
|
+
reader: server_reader,
|
46
|
+
writer: server_writer,
|
47
|
+
interaction_worker: interaction_worker,
|
48
|
+
signature_worker: signature_worker,
|
49
|
+
code_workers: code_workers
|
50
|
+
)
|
61
51
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
52
|
+
main_thread = Thread.start do
|
53
|
+
master.start()
|
54
|
+
end
|
55
|
+
main_thread.abort_on_exception = true
|
56
|
+
|
57
|
+
client_writer.write(method: "initialize", id: 0)
|
58
|
+
|
59
|
+
Steep.logger.info "Watching #{dirs.join(", ")}..."
|
60
|
+
listener = Listen.to(*dirs.map(&:to_s)) do |modified, added, removed|
|
61
|
+
stdout.puts "🔬 Type checking updated files..."
|
62
|
+
|
63
|
+
version = Time.now.to_i
|
64
|
+
Steep.logger.tagged "watch" do
|
65
|
+
Steep.logger.info "Received file system updates: modified=[#{modified.join(",")}], added=[#{added.join(",")}], removed=[#{removed.join(",")}]"
|
66
|
+
|
67
|
+
(modified + added).each do |path|
|
68
|
+
client_writer.write(
|
69
|
+
method: "textDocument/didChange",
|
70
|
+
params: {
|
71
|
+
textDocument: {
|
72
|
+
uri: "file://#{path}",
|
73
|
+
version: version
|
74
|
+
},
|
75
|
+
contentChanges: [
|
76
|
+
{
|
77
|
+
text: Pathname(path).read
|
78
|
+
}
|
79
|
+
]
|
80
|
+
}
|
81
|
+
)
|
66
82
|
end
|
67
|
-
end
|
68
83
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
84
|
+
removed.each do |path|
|
85
|
+
client_writer.write(
|
86
|
+
method: "textDocument/didChange",
|
87
|
+
params: {
|
88
|
+
textDocument: {
|
89
|
+
uri: "file://#{path}",
|
90
|
+
version: version
|
91
|
+
},
|
92
|
+
contentChanges: [
|
93
|
+
{
|
94
|
+
text: ""
|
95
|
+
}
|
96
|
+
]
|
97
|
+
}
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end.tap(&:start)
|
76
102
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
case
|
81
|
-
when
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
103
|
+
begin
|
104
|
+
stdout.puts "👀 Watching directories, Ctrl-C to stop."
|
105
|
+
client_reader.read do |response|
|
106
|
+
case response[:method]
|
107
|
+
when "textDocument/publishDiagnostics"
|
108
|
+
uri = URI.parse(response[:params][:uri])
|
109
|
+
path = project.relative_path(Pathname(uri.path))
|
110
|
+
|
111
|
+
diagnostics = response[:params][:diagnostics]
|
112
|
+
|
113
|
+
unless diagnostics.empty?
|
114
|
+
diagnostics.each do |diagnostic|
|
115
|
+
start = diagnostic[:range][:start]
|
116
|
+
loc = "#{start[:line]+1}:#{start[:character]}"
|
117
|
+
message = diagnostic[:message].chomp.lines.join(" ")
|
118
|
+
|
119
|
+
stdout.puts "#{path}:#{loc}: #{message}"
|
91
120
|
end
|
92
121
|
end
|
93
122
|
end
|
94
123
|
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def run()
|
99
|
-
if dirs.empty?
|
100
|
-
stdout.puts "Specify directories to watch"
|
101
|
-
return 1
|
102
|
-
end
|
103
|
-
|
104
|
-
project = load_config()
|
105
|
-
|
106
|
-
loader = Project::FileLoader.new(project: project)
|
107
|
-
loader.load_sources([])
|
108
|
-
loader.load_signatures()
|
109
|
-
|
110
|
-
type_check project
|
111
|
-
print_project_result project
|
112
|
-
|
113
|
-
listener.start
|
114
|
-
|
115
|
-
stdout.puts "👀 Watching directories, Ctrl-C to stop."
|
116
|
-
begin
|
117
|
-
type_check_loop project
|
118
124
|
rescue Interrupt
|
119
|
-
|
125
|
+
stdout.puts "Shutting down workers..."
|
126
|
+
client_writer.write({ method: :shutdown, id: 10000 })
|
127
|
+
client_writer.write({ method: :exit })
|
128
|
+
client_writer.io.close()
|
120
129
|
end
|
121
130
|
|
131
|
+
listener.stop
|
132
|
+
main_thread.join
|
133
|
+
|
122
134
|
0
|
123
135
|
end
|
124
136
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Steep
|
2
|
+
module Drivers
|
3
|
+
class Worker
|
4
|
+
attr_reader :stdout, :stderr, :stdin
|
5
|
+
|
6
|
+
attr_accessor :steepfile_path
|
7
|
+
attr_accessor :worker_type
|
8
|
+
attr_accessor :worker_name
|
9
|
+
|
10
|
+
include Utils::DriverHelper
|
11
|
+
|
12
|
+
def initialize(stdout:, stderr:, stdin:)
|
13
|
+
@stdout = stdout
|
14
|
+
@stderr = stderr
|
15
|
+
@stdin = stdin
|
16
|
+
end
|
17
|
+
|
18
|
+
def run()
|
19
|
+
Steep.logger.tagged("#{worker_type}:#{worker_name}") do
|
20
|
+
project = load_config()
|
21
|
+
|
22
|
+
loader = Project::FileLoader.new(project: project)
|
23
|
+
loader.load_sources([])
|
24
|
+
loader.load_signatures()
|
25
|
+
|
26
|
+
reader = LanguageServer::Protocol::Transport::Io::Reader.new(stdin)
|
27
|
+
writer = LanguageServer::Protocol::Transport::Io::Writer.new(stdout)
|
28
|
+
|
29
|
+
worker = case worker_type
|
30
|
+
when :code
|
31
|
+
Server::CodeWorker.new(project: project, reader: reader, writer: writer)
|
32
|
+
when :signature
|
33
|
+
Server::SignatureWorker.new(project: project, reader: reader, writer: writer)
|
34
|
+
when :interaction
|
35
|
+
Server::InteractionWorker.new(project: project, reader: reader, writer: writer)
|
36
|
+
else
|
37
|
+
raise "Unknown worker type: #{worker_type}"
|
38
|
+
end
|
39
|
+
|
40
|
+
Steep.logger.info "Starting #{worker_type} worker..."
|
41
|
+
|
42
|
+
worker.run()
|
43
|
+
rescue Interrupt
|
44
|
+
Steep.logger.info "Shutting down by interrupt..."
|
45
|
+
end
|
46
|
+
|
47
|
+
0
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -47,8 +47,10 @@ module Steep
|
|
47
47
|
position = Position.new(line: line, column: column)
|
48
48
|
|
49
49
|
begin
|
50
|
-
Steep.
|
51
|
-
type_check!
|
50
|
+
Steep.logger.tagged "completion_provider#run(line: #{line}, column: #{column})" do
|
51
|
+
Steep.measure "type_check!" do
|
52
|
+
type_check!(source_text)
|
53
|
+
end
|
52
54
|
end
|
53
55
|
|
54
56
|
Steep.measure "completion item collection" do
|
data/lib/steep/project/file.rb
CHANGED
@@ -33,9 +33,12 @@ module Steep
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def content_for(path:, line:, column:)
|
36
|
-
|
36
|
+
target = project.targets.find {|target| target.source_file?(path) }
|
37
|
+
|
38
|
+
if target
|
39
|
+
source_file = target.source_files[path]
|
40
|
+
target.type_check(target_sources: [source_file], validate_signatures: false)
|
37
41
|
|
38
|
-
if source_file
|
39
42
|
case (status = source_file.status)
|
40
43
|
when SourceFile::TypeCheckStatus
|
41
44
|
node, *parents = status.source.find_nodes(line: line, column: column)
|
data/lib/steep/project/target.rb
CHANGED
@@ -67,24 +67,30 @@ module Steep
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def possible_source_file?(path)
|
70
|
-
self.class.test_pattern(source_patterns, path) &&
|
71
|
-
!self.class.test_pattern(ignore_patterns, path)
|
70
|
+
self.class.test_pattern(source_patterns, path, ext: ".rb") &&
|
71
|
+
!self.class.test_pattern(ignore_patterns, path, ext: ".rb")
|
72
72
|
end
|
73
73
|
|
74
74
|
def possible_signature_file?(path)
|
75
|
-
self.class.test_pattern(signature_patterns, path)
|
75
|
+
self.class.test_pattern(signature_patterns, path, ext: ".rbs")
|
76
76
|
end
|
77
77
|
|
78
|
-
def self.test_pattern(patterns, path)
|
78
|
+
def self.test_pattern(patterns, path, ext:)
|
79
79
|
patterns.any? do |pattern|
|
80
80
|
p = pattern.end_with?(File::Separator) ? pattern : pattern + File::Separator
|
81
|
-
path.to_s.start_with?(p) || File.fnmatch(pattern, path.to_s)
|
81
|
+
(path.to_s.start_with?(p) && path.extname == ext) || File.fnmatch(pattern, path.to_s)
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
|
-
def type_check
|
86
|
-
|
87
|
-
|
85
|
+
def type_check(target_sources: source_files.values, validate_signatures: true)
|
86
|
+
Steep.logger.tagged "target#type_check(target_sources: [#{target_sources.map(&:path).join(", ")}], validate_signatures: #{validate_signatures})" do
|
87
|
+
Steep.measure "load signature and type check" do
|
88
|
+
load_signatures(validate: validate_signatures) do |env, check, timestamp|
|
89
|
+
Steep.measure "type checking #{target_sources.size} files" do
|
90
|
+
run_type_check(env, check, timestamp, target_sources: target_sources)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
88
94
|
end
|
89
95
|
end
|
90
96
|
|
@@ -100,7 +106,7 @@ module Steep
|
|
100
106
|
end
|
101
107
|
end
|
102
108
|
|
103
|
-
def load_signatures
|
109
|
+
def load_signatures(validate:)
|
104
110
|
timestamp = case status
|
105
111
|
when TypeCheckStatus
|
106
112
|
status.timestamp
|
@@ -133,16 +139,20 @@ module Steep
|
|
133
139
|
factory = AST::Types::Factory.new(builder: definition_builder)
|
134
140
|
check = Subtyping::Check.new(factory: factory)
|
135
141
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
142
|
+
if validate
|
143
|
+
validator = Signature::Validator.new(checker: check)
|
144
|
+
validator.validate()
|
145
|
+
|
146
|
+
if validator.no_error?
|
147
|
+
yield env, check, Time.now
|
148
|
+
else
|
149
|
+
@status = SignatureValidationErrorStatus.new(
|
150
|
+
errors: validator.each_error.to_a,
|
151
|
+
timestamp: Time.now
|
152
|
+
)
|
153
|
+
end
|
141
154
|
else
|
142
|
-
|
143
|
-
errors: validator.each_error.to_a,
|
144
|
-
timestamp: Time.now
|
145
|
-
)
|
155
|
+
yield env, check, Time.now
|
146
156
|
end
|
147
157
|
end
|
148
158
|
|
@@ -160,10 +170,10 @@ module Steep
|
|
160
170
|
end
|
161
171
|
end
|
162
172
|
|
163
|
-
def run_type_check(env, check, timestamp)
|
173
|
+
def run_type_check(env, check, timestamp, target_sources: source_files.values)
|
164
174
|
type_check_sources = []
|
165
175
|
|
166
|
-
|
176
|
+
target_sources.each do |file|
|
167
177
|
if file.type_check(check, timestamp)
|
168
178
|
type_check_sources << file
|
169
179
|
end
|
data/lib/steep/project.rb
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
module Steep
|
2
2
|
class Project
|
3
3
|
attr_reader :targets
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :steepfile_path
|
5
5
|
|
6
|
-
def initialize(
|
6
|
+
def initialize(steepfile_path:)
|
7
7
|
@targets = []
|
8
|
-
@
|
8
|
+
@steepfile_path = steepfile_path
|
9
9
|
|
10
|
-
unless
|
11
|
-
raise "Project#initialize(
|
10
|
+
unless steepfile_path.absolute?
|
11
|
+
raise "Project#initialize(steepfile_path:): steepfile_path should be absolute path"
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
def base_dir
|
16
|
+
steepfile_path.parent
|
17
|
+
end
|
18
|
+
|
15
19
|
def relative_path(path)
|
16
20
|
path.relative_path_from(base_dir)
|
17
21
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Steep
|
2
|
+
module Server
|
3
|
+
class BaseWorker
|
4
|
+
LSP = LanguageServer::Protocol
|
5
|
+
|
6
|
+
include Utils
|
7
|
+
|
8
|
+
attr_reader :project
|
9
|
+
attr_reader :reader, :writer
|
10
|
+
|
11
|
+
def initialize(project:, reader:, writer:)
|
12
|
+
@project = project
|
13
|
+
@reader = reader
|
14
|
+
@writer = writer
|
15
|
+
end
|
16
|
+
|
17
|
+
def handle_request(request)
|
18
|
+
# process request
|
19
|
+
end
|
20
|
+
|
21
|
+
def handle_job(job)
|
22
|
+
# process async job
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
tags = Steep.logger.formatter.current_tags.dup
|
27
|
+
thread = Thread.new do
|
28
|
+
Steep.logger.formatter.push_tags(*tags)
|
29
|
+
Steep.logger.tagged "background" do
|
30
|
+
while job = queue.pop
|
31
|
+
handle_job(job)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Steep.logger.tagged "frontend" do
|
37
|
+
begin
|
38
|
+
reader.read do |request|
|
39
|
+
case request[:method]
|
40
|
+
when "shutdown"
|
41
|
+
# nop
|
42
|
+
when "exit"
|
43
|
+
break
|
44
|
+
else
|
45
|
+
handle_request(request)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
ensure
|
49
|
+
queue << nil
|
50
|
+
thread.join
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module Steep
|
2
|
+
module Server
|
3
|
+
class CodeWorker < BaseWorker
|
4
|
+
LSP = LanguageServer::Protocol
|
5
|
+
|
6
|
+
include Utils
|
7
|
+
|
8
|
+
attr_reader :target_files
|
9
|
+
attr_reader :queue
|
10
|
+
|
11
|
+
def initialize(project:, reader:, writer:, queue: Queue.new)
|
12
|
+
super(project: project, reader: reader, writer: writer)
|
13
|
+
|
14
|
+
@target_files = {}
|
15
|
+
@queue = queue
|
16
|
+
end
|
17
|
+
|
18
|
+
def enqueue_type_check(target:, path:, version: target_files[path])
|
19
|
+
Steep.logger.info "Enqueueing type check: #{path}(#{version})@#{target.name}..."
|
20
|
+
target_files[path] = version
|
21
|
+
queue << [path, version, target]
|
22
|
+
end
|
23
|
+
|
24
|
+
def each_type_check_subject(path:, version:)
|
25
|
+
case
|
26
|
+
when !(updated_targets = project.targets.select {|target| target.signature_file?(path) }).empty?
|
27
|
+
updated_targets.each do |target|
|
28
|
+
target_files.each_key do |path|
|
29
|
+
if target.source_file?(path)
|
30
|
+
yield target, path, target_files[path]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
when target = project.targets.find {|target| target.source_file?(path) }
|
36
|
+
if target_files.key?(path)
|
37
|
+
yield target, path, version
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def typecheck_file(path, target)
|
43
|
+
Steep.logger.info "Starting type checking: #{path}@#{target.name}..."
|
44
|
+
|
45
|
+
source = target.source_files[path]
|
46
|
+
target.type_check(target_sources: [source], validate_signatures: false)
|
47
|
+
|
48
|
+
Steep.logger.info "Finished type checking: #{path}@#{target.name}"
|
49
|
+
|
50
|
+
diagnostics = source_diagnostics(source)
|
51
|
+
|
52
|
+
writer.write(
|
53
|
+
method: :"textDocument/publishDiagnostics",
|
54
|
+
params: LSP::Interface::PublishDiagnosticsParams.new(
|
55
|
+
uri: URI.parse(project.absolute_path(path).to_s).tap {|uri| uri.scheme = "file"},
|
56
|
+
diagnostics: diagnostics
|
57
|
+
)
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
def source_diagnostics(source)
|
62
|
+
case status = source.status
|
63
|
+
when Project::SourceFile::ParseErrorStatus
|
64
|
+
[]
|
65
|
+
when Project::SourceFile::AnnotationSyntaxErrorStatus
|
66
|
+
[
|
67
|
+
LSP::Interface::Diagnostic.new(
|
68
|
+
message: "Annotation syntax error: #{status.error.cause.message}",
|
69
|
+
severity: LSP::Constant::DiagnosticSeverity::ERROR,
|
70
|
+
range: LSP::Interface::Range.new(
|
71
|
+
start: LSP::Interface::Position.new(
|
72
|
+
line: status.location.start_line - 1,
|
73
|
+
character: status.location.start_column
|
74
|
+
),
|
75
|
+
end: LSP::Interface::Position.new(
|
76
|
+
line: status.location.end_line - 1,
|
77
|
+
character: status.location.end_column
|
78
|
+
)
|
79
|
+
)
|
80
|
+
)
|
81
|
+
]
|
82
|
+
when Project::SourceFile::TypeCheckStatus
|
83
|
+
status.typing.errors.map do |error|
|
84
|
+
loc = error.location_to_str
|
85
|
+
|
86
|
+
LSP::Interface::Diagnostic.new(
|
87
|
+
message: StringIO.new.tap {|io| error.print_to(io) }.string.gsub(/\A#{loc}: /, "").chomp,
|
88
|
+
severity: LSP::Constant::DiagnosticSeverity::ERROR,
|
89
|
+
range: LSP::Interface::Range.new(
|
90
|
+
start: LSP::Interface::Position.new(
|
91
|
+
line: error.node.loc.line - 1,
|
92
|
+
character: error.node.loc.column
|
93
|
+
),
|
94
|
+
end: LSP::Interface::Position.new(
|
95
|
+
line: error.node.loc.last_line - 1,
|
96
|
+
character: error.node.loc.last_column
|
97
|
+
)
|
98
|
+
)
|
99
|
+
)
|
100
|
+
end
|
101
|
+
when Project::SourceFile::TypeCheckErrorStatus
|
102
|
+
[]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def handle_request(request)
|
107
|
+
case request[:method]
|
108
|
+
when "initialize"
|
109
|
+
# Don't respond to initialize request, but start type checking.
|
110
|
+
project.targets.each do |target|
|
111
|
+
target.source_files.each_key do |path|
|
112
|
+
if target_files.key?(path)
|
113
|
+
enqueue_type_check(target: target, path: path, version: target_files[path])
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
when "workspace/executeCommand"
|
119
|
+
if request[:params][:command] == "steep/registerSourceToWorker"
|
120
|
+
paths = request[:params][:arguments].map {|arg| source_path(URI.parse(arg)) }
|
121
|
+
paths.each do |path|
|
122
|
+
target_files[path] = 0
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
when "textDocument/didChange"
|
127
|
+
update_source(request) do |path, version|
|
128
|
+
if target_files.key?(path)
|
129
|
+
target_files[path] = version
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
path = source_path(URI.parse(request[:params][:textDocument][:uri]))
|
134
|
+
version = request[:params][:textDocument][:version]
|
135
|
+
each_type_check_subject(path: path, version: version) do |target, path, version|
|
136
|
+
enqueue_type_check(target: target, path: path, version: version)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def handle_job(job)
|
142
|
+
path, version, target = job
|
143
|
+
if !version || target_files[path] == version
|
144
|
+
typecheck_file(path, target)
|
145
|
+
else
|
146
|
+
Steep.logger.info "Skipping type check: #{path}@#{target.name}, queued version=#{version}, latest version=#{target_files[path]}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|