steep 0.39.0 → 0.40.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/.github/workflows/ruby.yml +1 -1
- data/CHANGELOG.md +6 -0
- data/Rakefile +5 -2
- data/bin/output_rebaseline.rb +49 -0
- data/bin/output_test.rb +93 -0
- data/lib/steep.rb +8 -3
- data/lib/steep/cli.rb +1 -1
- data/lib/steep/diagnostic/helper.rb +17 -0
- data/lib/steep/diagnostic/lsp_formatter.rb +16 -0
- data/lib/steep/diagnostic/ruby.rb +623 -0
- data/lib/steep/diagnostic/signature.rb +224 -0
- data/lib/steep/drivers/annotations.rb +13 -6
- data/lib/steep/drivers/check.rb +83 -60
- data/lib/steep/drivers/diagnostic_printer.rb +94 -0
- data/lib/steep/drivers/stats.rb +125 -29
- data/lib/steep/drivers/trace_printer.rb +5 -1
- data/lib/steep/drivers/validate.rb +13 -6
- data/lib/steep/drivers/watch.rb +26 -9
- data/lib/steep/drivers/worker.rb +5 -0
- data/lib/steep/project/options.rb +4 -4
- data/lib/steep/project/signature_file.rb +8 -2
- data/lib/steep/project/stats_calculator.rb +80 -0
- data/lib/steep/project/target.rb +64 -53
- data/lib/steep/range_extension.rb +29 -0
- data/lib/steep/server/base_worker.rb +42 -4
- data/lib/steep/server/code_worker.rb +37 -24
- data/lib/steep/server/interaction_worker.rb +1 -0
- data/lib/steep/server/master.rb +268 -82
- data/lib/steep/server/signature_worker.rb +7 -59
- data/lib/steep/server/worker_process.rb +9 -9
- data/lib/steep/signature/validator.rb +33 -9
- data/lib/steep/type_construction.rb +276 -194
- data/lib/steep/version.rb +1 -1
- data/smoke/alias/a.rb +0 -3
- data/smoke/alias/b.rb +0 -1
- data/smoke/alias/c.rb +0 -2
- data/smoke/alias/test.yaml +73 -0
- data/smoke/and/a.rb +0 -3
- data/smoke/and/test.yaml +24 -0
- data/smoke/array/a.rb +0 -3
- data/smoke/array/b.rb +0 -2
- data/smoke/array/c.rb +0 -1
- data/smoke/array/test.yaml +80 -0
- data/smoke/block/a.rb +0 -2
- data/smoke/block/b.rb +0 -2
- data/smoke/block/d.rb +0 -4
- data/smoke/block/test.yaml +96 -0
- data/smoke/broken/Steepfile +5 -0
- data/smoke/broken/broken.rb +0 -0
- data/smoke/broken/broken.rbs +0 -0
- data/smoke/broken/test.yaml +6 -0
- data/smoke/case/a.rb +0 -3
- data/smoke/case/test.yaml +36 -0
- data/smoke/class/a.rb +0 -3
- data/smoke/class/c.rb +0 -1
- data/smoke/class/f.rb +0 -1
- data/smoke/class/g.rb +0 -2
- data/smoke/class/i.rb +0 -2
- data/smoke/class/test.yaml +89 -0
- data/smoke/const/a.rb +0 -3
- data/smoke/const/b.rb +7 -0
- data/smoke/const/b.rbs +5 -0
- data/smoke/const/test.yaml +96 -0
- data/smoke/diagnostics-rbs-duplicated/Steepfile +5 -0
- data/smoke/diagnostics-rbs-duplicated/a.rbs +5 -0
- data/smoke/diagnostics-rbs-duplicated/test.yaml +10 -0
- data/smoke/diagnostics-rbs/Steepfile +5 -0
- data/smoke/diagnostics-rbs/duplicated-method-definition.rbs +20 -0
- data/smoke/diagnostics-rbs/generic-parameter-mismatch.rbs +7 -0
- data/smoke/diagnostics-rbs/invalid-method-overload.rbs +3 -0
- data/smoke/diagnostics-rbs/invalid-type-application.rbs +7 -0
- data/smoke/diagnostics-rbs/invalid_variance_annotation.rbs +3 -0
- data/smoke/diagnostics-rbs/recursive-alias.rbs +5 -0
- data/smoke/diagnostics-rbs/recursive-class.rbs +8 -0
- data/smoke/diagnostics-rbs/superclass-mismatch.rbs +7 -0
- data/smoke/diagnostics-rbs/test.yaml +142 -0
- data/smoke/diagnostics-rbs/unknown-method-alias.rbs +3 -0
- data/smoke/diagnostics-rbs/unknown-type-name.rbs +13 -0
- data/smoke/diagnostics/Steepfile +5 -0
- data/smoke/diagnostics/a.rbs +26 -0
- data/smoke/diagnostics/argument_type_mismatch.rb +1 -0
- data/smoke/diagnostics/block_body_type_mismatch.rb +1 -0
- data/smoke/diagnostics/block_type_mismatch.rb +3 -0
- data/smoke/diagnostics/break_type_mismatch.rb +1 -0
- data/smoke/diagnostics/else_on_exhaustive_case.rb +12 -0
- data/smoke/diagnostics/incompatible_annotation.rb +6 -0
- data/smoke/diagnostics/incompatible_argument.rb +1 -0
- data/smoke/diagnostics/incompatible_assignment.rb +8 -0
- data/smoke/diagnostics/method_arity_mismatch.rb +11 -0
- data/smoke/diagnostics/method_body_type_mismatch.rb +6 -0
- data/smoke/diagnostics/method_definition_missing.rb +2 -0
- data/smoke/diagnostics/method_return_type_annotation_mismatch.rb +7 -0
- data/smoke/diagnostics/missing_keyword.rb +1 -0
- data/smoke/diagnostics/no_method.rb +1 -0
- data/smoke/diagnostics/required_block_missing.rb +1 -0
- data/smoke/diagnostics/return_type_mismatch.rb +6 -0
- data/smoke/diagnostics/test.yaml +333 -0
- data/smoke/diagnostics/unexpected_block_given.rb +1 -0
- data/smoke/diagnostics/unexpected_dynamic_method.rb +3 -0
- data/smoke/diagnostics/unexpected_jump.rb +4 -0
- data/smoke/diagnostics/unexpected_jump_value.rb +3 -0
- data/smoke/diagnostics/unexpected_keyword.rb +1 -0
- data/smoke/diagnostics/unexpected_splat.rb +1 -0
- data/smoke/diagnostics/unexpected_yield.rb +6 -0
- data/smoke/diagnostics/unknown_constant_assigned.rb +7 -0
- data/smoke/diagnostics/unresolved_overloading.rb +1 -0
- data/smoke/diagnostics/unsatisfiable_constraint.rb +7 -0
- data/smoke/diagnostics/unsupported_syntax.rb +2 -0
- data/smoke/dstr/a.rb +0 -1
- data/smoke/dstr/test.yaml +10 -0
- data/smoke/ensure/a.rb +0 -4
- data/smoke/ensure/test.yaml +47 -0
- data/smoke/enumerator/a.rb +0 -6
- data/smoke/enumerator/b.rb +0 -3
- data/smoke/enumerator/test.yaml +100 -0
- data/smoke/extension/a.rb +0 -1
- data/smoke/extension/b.rb +0 -2
- data/smoke/extension/c.rb +0 -1
- data/smoke/extension/test.yaml +50 -0
- data/smoke/hash/b.rb +0 -1
- data/smoke/hash/c.rb +0 -3
- data/smoke/hash/d.rb +0 -1
- data/smoke/hash/e.rb +0 -1
- data/smoke/hash/test.yaml +62 -0
- data/smoke/hello/hello.rb +0 -2
- data/smoke/hello/test.yaml +18 -0
- data/smoke/if/a.rb +0 -2
- data/smoke/if/test.yaml +27 -0
- data/smoke/implements/a.rb +0 -2
- data/smoke/implements/test.yaml +16 -0
- data/smoke/initialize/test.yaml +4 -0
- data/smoke/integer/a.rb +0 -7
- data/smoke/integer/test.yaml +66 -0
- data/smoke/interface/a.rb +0 -2
- data/smoke/interface/test.yaml +16 -0
- data/smoke/kwbegin/a.rb +0 -1
- data/smoke/kwbegin/test.yaml +14 -0
- data/smoke/lambda/a.rb +1 -4
- data/smoke/lambda/test.yaml +28 -0
- data/smoke/literal/a.rb +0 -5
- data/smoke/literal/b.rb +0 -2
- data/smoke/literal/test.yaml +79 -0
- data/smoke/map/test.yaml +4 -0
- data/smoke/method/a.rb +0 -5
- data/smoke/method/b.rb +0 -1
- data/smoke/method/test.yaml +71 -0
- data/smoke/module/a.rb +0 -2
- data/smoke/module/b.rb +0 -2
- data/smoke/module/c.rb +0 -1
- data/smoke/module/d.rb +0 -1
- data/smoke/module/f.rb +0 -2
- data/smoke/module/test.yaml +51 -0
- data/smoke/regexp/a.rb +0 -38
- data/smoke/regexp/b.rb +0 -26
- data/smoke/regexp/test.yaml +372 -0
- data/smoke/regression/set_divide.rb +0 -4
- data/smoke/regression/test.yaml +38 -0
- data/smoke/rescue/a.rb +0 -5
- data/smoke/rescue/test.yaml +60 -0
- data/smoke/self/a.rb +0 -2
- data/smoke/self/test.yaml +16 -0
- data/smoke/skip/skip.rb +0 -2
- data/smoke/skip/test.yaml +16 -0
- data/smoke/stdout/test.yaml +4 -0
- data/smoke/super/a.rb +0 -4
- data/smoke/super/test.yaml +52 -0
- data/smoke/toplevel/a.rb +0 -1
- data/smoke/toplevel/test.yaml +12 -0
- data/smoke/tsort/a.rb +0 -3
- data/smoke/tsort/test.yaml +32 -0
- data/smoke/type_case/a.rb +0 -4
- data/smoke/type_case/test.yaml +33 -0
- data/smoke/yield/a.rb +0 -3
- data/smoke/yield/b.rb +6 -0
- data/smoke/yield/test.yaml +49 -0
- data/steep.gemspec +3 -3
- metadata +108 -17
- data/bin/smoke_runner.rb +0 -139
- data/lib/steep/drivers/signature_error_printer.rb +0 -25
- data/lib/steep/errors.rb +0 -594
- data/lib/steep/signature/errors.rb +0 -128
- data/lib/steep/type_assignability.rb +0 -367
@@ -0,0 +1,29 @@
|
|
1
|
+
class RBS::Location
|
2
|
+
def as_lsp_range
|
3
|
+
{
|
4
|
+
start: {
|
5
|
+
line: start_line - 1,
|
6
|
+
character: start_column
|
7
|
+
},
|
8
|
+
end: {
|
9
|
+
line: end_line - 1,
|
10
|
+
character: end_column
|
11
|
+
}
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Parser::Source::Range
|
17
|
+
def as_lsp_range
|
18
|
+
{
|
19
|
+
start: {
|
20
|
+
line: line - 1,
|
21
|
+
character: column
|
22
|
+
},
|
23
|
+
end: {
|
24
|
+
line: last_line - 1,
|
25
|
+
character: last_column
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
@@ -8,13 +8,28 @@ module Steep
|
|
8
8
|
attr_reader :project
|
9
9
|
attr_reader :reader, :writer
|
10
10
|
|
11
|
+
ShutdownJob = Struct.new(:id, keyword_init: true)
|
12
|
+
|
11
13
|
def initialize(project:, reader:, writer:)
|
12
14
|
@project = project
|
13
15
|
@reader = reader
|
14
16
|
@writer = writer
|
17
|
+
@skip_job = false
|
15
18
|
@shutdown = false
|
16
19
|
end
|
17
20
|
|
21
|
+
def skip_jobs_after_shutdown!(flag = true)
|
22
|
+
@skip_jobs_after_shutdown = flag
|
23
|
+
end
|
24
|
+
|
25
|
+
def skip_jobs_after_shutdown?
|
26
|
+
@skip_jobs_after_shutdown
|
27
|
+
end
|
28
|
+
|
29
|
+
def skip_job?
|
30
|
+
@skip_job
|
31
|
+
end
|
32
|
+
|
18
33
|
def handle_request(request)
|
19
34
|
# process request
|
20
35
|
end
|
@@ -29,7 +44,29 @@ module Steep
|
|
29
44
|
Steep.logger.formatter.push_tags(*tags)
|
30
45
|
Steep.logger.tagged "background" do
|
31
46
|
while job = queue.pop
|
32
|
-
|
47
|
+
case job
|
48
|
+
when ShutdownJob
|
49
|
+
writer.write(id: job.id, result: nil)
|
50
|
+
else
|
51
|
+
if skip_job?
|
52
|
+
Steep.logger.info "Skipping job..."
|
53
|
+
else
|
54
|
+
begin
|
55
|
+
handle_job(job)
|
56
|
+
rescue => exn
|
57
|
+
Steep.log_error exn
|
58
|
+
writer.write(
|
59
|
+
{
|
60
|
+
method: "window/showMessage",
|
61
|
+
params: {
|
62
|
+
type: LSP::Constant::MessageType::ERROR,
|
63
|
+
message: "Unexpected error: #{exn.message} (#{exn.class})"
|
64
|
+
}
|
65
|
+
}
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
33
70
|
end
|
34
71
|
end
|
35
72
|
end
|
@@ -37,10 +74,12 @@ module Steep
|
|
37
74
|
Steep.logger.tagged "frontend" do
|
38
75
|
begin
|
39
76
|
reader.read do |request|
|
77
|
+
Steep.logger.info "Received message from master: #{request[:method]}(#{request[:id]})"
|
40
78
|
case request[:method]
|
41
79
|
when "shutdown"
|
42
|
-
|
43
|
-
|
80
|
+
queue << ShutdownJob.new(id: request[:id])
|
81
|
+
@skip_job = skip_jobs_after_shutdown?
|
82
|
+
queue.close
|
44
83
|
when "exit"
|
45
84
|
break
|
46
85
|
else
|
@@ -48,7 +87,6 @@ module Steep
|
|
48
87
|
end
|
49
88
|
end
|
50
89
|
ensure
|
51
|
-
queue << nil
|
52
90
|
thread.join
|
53
91
|
end
|
54
92
|
end
|
@@ -3,6 +3,9 @@ module Steep
|
|
3
3
|
class CodeWorker < BaseWorker
|
4
4
|
LSP = LanguageServer::Protocol
|
5
5
|
|
6
|
+
TypeCheckJob = Struct.new(:target, :path, keyword_init: true)
|
7
|
+
StatsJob = Struct.new(:request, :paths, keyword_init: true)
|
8
|
+
|
6
9
|
include Utils
|
7
10
|
|
8
11
|
attr_reader :typecheck_paths
|
@@ -17,7 +20,7 @@ module Steep
|
|
17
20
|
|
18
21
|
def enqueue_type_check(target:, path:)
|
19
22
|
Steep.logger.info "Enqueueing type check: #{target.name}::#{path}..."
|
20
|
-
queue <<
|
23
|
+
queue << TypeCheckJob.new(target: target, path: path)
|
21
24
|
end
|
22
25
|
|
23
26
|
def typecheck_file(path, target)
|
@@ -43,6 +46,23 @@ module Steep
|
|
43
46
|
)
|
44
47
|
end
|
45
48
|
|
49
|
+
def calculate_stats(request_id, paths)
|
50
|
+
calculator = Project::StatsCalculator.new(project: project)
|
51
|
+
|
52
|
+
stats = paths.map do |path|
|
53
|
+
if typecheck_paths.include?(path)
|
54
|
+
if target = project.target_for_source_path(path)
|
55
|
+
calculator.calc_stats(target, path)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end.compact
|
59
|
+
|
60
|
+
writer.write(
|
61
|
+
id: request_id,
|
62
|
+
result: stats.map(&:as_json)
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
46
66
|
def source_diagnostics(source, options)
|
47
67
|
case status = source.status
|
48
68
|
when Project::SourceFile::ParseErrorStatus
|
@@ -65,24 +85,8 @@ module Steep
|
|
65
85
|
)
|
66
86
|
]
|
67
87
|
when Project::SourceFile::TypeCheckStatus
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
LSP::Interface::Diagnostic.new(
|
72
|
-
message: StringIO.new.tap {|io| error.print_to(io) }.string.gsub(/\A#{Regexp.escape(loc)}: /, "").chomp,
|
73
|
-
severity: LSP::Constant::DiagnosticSeverity::ERROR,
|
74
|
-
range: LSP::Interface::Range.new(
|
75
|
-
start: LSP::Interface::Position.new(
|
76
|
-
line: error.node.loc.line - 1,
|
77
|
-
character: error.node.loc.column
|
78
|
-
),
|
79
|
-
end: LSP::Interface::Position.new(
|
80
|
-
line: error.node.loc.last_line - 1,
|
81
|
-
character: error.node.loc.last_column
|
82
|
-
)
|
83
|
-
)
|
84
|
-
)
|
85
|
-
end
|
88
|
+
formatter = Diagnostic::LSPFormatter.new()
|
89
|
+
status.typing.errors.select {|error| options.error_to_report?(error) }.map {|error| formatter.format(error) }
|
86
90
|
when Project::SourceFile::TypeCheckErrorStatus
|
87
91
|
[]
|
88
92
|
end
|
@@ -91,7 +95,6 @@ module Steep
|
|
91
95
|
def handle_request(request)
|
92
96
|
case request[:method]
|
93
97
|
when "initialize"
|
94
|
-
# Don't respond to initialize request, but start type checking.
|
95
98
|
project.targets.each do |target|
|
96
99
|
target.source_files.each_key do |path|
|
97
100
|
if typecheck_paths.include?(path)
|
@@ -100,10 +103,17 @@ module Steep
|
|
100
103
|
end
|
101
104
|
end
|
102
105
|
|
106
|
+
writer.write({ id: request[:id], result: nil })
|
107
|
+
|
103
108
|
when "workspace/executeCommand"
|
104
|
-
|
109
|
+
Steep.logger.info { "Executing command: #{request[:params][:command]}, arguments=#{request[:params][:arguments].map(&:inspect).join(", ")}" }
|
110
|
+
case request[:params][:command]
|
111
|
+
when "steep/registerSourceToWorker"
|
105
112
|
paths = request[:params][:arguments].map {|arg| source_path(URI.parse(arg)) }
|
106
113
|
typecheck_paths.merge(paths)
|
114
|
+
when "steep/stats"
|
115
|
+
paths = request[:params][:arguments].map {|arg| source_path(URI.parse(arg)) }
|
116
|
+
queue << StatsJob.new(paths: paths, request: request)
|
107
117
|
end
|
108
118
|
|
109
119
|
when "textDocument/didChange"
|
@@ -128,9 +138,12 @@ module Steep
|
|
128
138
|
end
|
129
139
|
|
130
140
|
def handle_job(job)
|
131
|
-
|
132
|
-
|
133
|
-
|
141
|
+
case job
|
142
|
+
when TypeCheckJob
|
143
|
+
typecheck_file(job.path, job.target)
|
144
|
+
when StatsJob
|
145
|
+
calculate_stats(job.request[:id], job.paths)
|
146
|
+
end
|
134
147
|
end
|
135
148
|
end
|
136
149
|
end
|
data/lib/steep/server/master.rb
CHANGED
@@ -6,7 +6,6 @@ module Steep
|
|
6
6
|
attr_reader :steepfile
|
7
7
|
attr_reader :project
|
8
8
|
attr_reader :reader, :writer
|
9
|
-
attr_reader :queue
|
10
9
|
attr_reader :worker_count
|
11
10
|
attr_reader :worker_to_paths
|
12
11
|
|
@@ -14,75 +13,179 @@ module Steep
|
|
14
13
|
attr_reader :signature_worker
|
15
14
|
attr_reader :code_workers
|
16
15
|
|
16
|
+
attr_reader :response_handlers
|
17
|
+
|
18
|
+
# There are four types of threads:
|
19
|
+
#
|
20
|
+
# 1. Main thread -- Reads messages from client
|
21
|
+
# 2. Worker threads -- Reads messages from associated worker
|
22
|
+
# 3. Reconciliation thread -- Receives message from worker threads, reconciles, processes, and forwards to write thread
|
23
|
+
# 4. Write thread -- Writes messages to client
|
24
|
+
#
|
25
|
+
# We have two queues:
|
26
|
+
#
|
27
|
+
# 1. `recon_queue` is to pass messages from worker threads to reconciliation thread
|
28
|
+
# 2. `write` thread is to pass messages to write thread
|
29
|
+
#
|
30
|
+
# Message passing: Client -> Server (Master) -> Worker
|
31
|
+
#
|
32
|
+
# 1. Client -> Server
|
33
|
+
# Master receives messages from the LSP client on main thread.
|
34
|
+
#
|
35
|
+
# 2. Master -> Worker
|
36
|
+
# Master writes messages to workers on main thread.
|
37
|
+
#
|
38
|
+
# Message passing: Worker -> Server (Master) -> (reconciliation queue) -> (write queue) -> Client
|
39
|
+
#
|
40
|
+
# 3. Worker -> Master
|
41
|
+
# Master receives messages on threads dedicated for each worker.
|
42
|
+
# The messages sent from workers are then forwarded to the reconciliation thread through reconciliation queue.
|
43
|
+
#
|
44
|
+
# 4. Server -> Client
|
45
|
+
# The reconciliation thread reads messages from reconciliation queue, does something, and finally sends messages to the client via write queue.
|
46
|
+
#
|
47
|
+
attr_reader :write_queue
|
48
|
+
attr_reader :recon_queue
|
49
|
+
|
17
50
|
include Utils
|
18
51
|
|
52
|
+
class ResponseHandler
|
53
|
+
attr_reader :workers
|
54
|
+
|
55
|
+
attr_reader :request
|
56
|
+
attr_reader :responses
|
57
|
+
|
58
|
+
attr_reader :on_response_handlers
|
59
|
+
attr_reader :on_completion_handlers
|
60
|
+
|
61
|
+
def initialize(request:, workers:)
|
62
|
+
@workers = []
|
63
|
+
|
64
|
+
@request = request
|
65
|
+
@responses = workers.each.with_object({}) do |worker, hash|
|
66
|
+
hash[worker] = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
@on_response_handlers = []
|
70
|
+
@on_completion_handlers = []
|
71
|
+
end
|
72
|
+
|
73
|
+
def on_response(&block)
|
74
|
+
on_response_handlers << block
|
75
|
+
end
|
76
|
+
|
77
|
+
def on_completion(&block)
|
78
|
+
on_completion_handlers << block
|
79
|
+
end
|
80
|
+
|
81
|
+
def request_id
|
82
|
+
request[:id]
|
83
|
+
end
|
84
|
+
|
85
|
+
def process_response(response, worker)
|
86
|
+
responses[worker] = response
|
87
|
+
|
88
|
+
on_response_handlers.each do |handler|
|
89
|
+
handler[worker, response]
|
90
|
+
end
|
91
|
+
|
92
|
+
if completed?
|
93
|
+
on_completion_handlers.each do |handler|
|
94
|
+
handler[*responses.values]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def completed?
|
100
|
+
responses.each_value.none?(&:nil?)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
19
104
|
def initialize(project:, reader:, writer:, interaction_worker:, signature_worker:, code_workers:, queue: Queue.new)
|
20
105
|
@project = project
|
21
106
|
@reader = reader
|
22
107
|
@writer = writer
|
23
|
-
@
|
108
|
+
@write_queue = queue
|
109
|
+
@recon_queue = Queue.new
|
24
110
|
@interaction_worker = interaction_worker
|
25
111
|
@signature_worker = signature_worker
|
26
112
|
@code_workers = code_workers
|
27
113
|
@worker_to_paths = {}
|
28
114
|
@shutdown_request_id = nil
|
115
|
+
@response_handlers = {}
|
29
116
|
end
|
30
117
|
|
31
118
|
def start
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
119
|
+
Steep.logger.tagged "master" do
|
120
|
+
tags = Steep.logger.formatter.current_tags.dup
|
121
|
+
|
122
|
+
Steep.logger.info "Registering all code to workers..."
|
123
|
+
source_paths = project.all_source_files
|
124
|
+
bin_size = (source_paths.size / code_workers.size) + 1
|
125
|
+
source_paths.each_slice(bin_size).with_index do |paths, index|
|
126
|
+
register_code_to_worker(paths, worker: code_workers[index])
|
127
|
+
end
|
37
128
|
|
38
|
-
|
39
|
-
tags << "master"
|
129
|
+
worker_threads = []
|
40
130
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
131
|
+
worker_threads << Thread.new do
|
132
|
+
Steep.logger.formatter.push_tags(*tags, "from-worker@interaction")
|
133
|
+
interaction_worker.reader.read do |message|
|
134
|
+
process_message_from_worker(message, worker: interaction_worker)
|
135
|
+
end
|
45
136
|
end
|
46
|
-
end
|
47
137
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
138
|
+
worker_threads << Thread.new do
|
139
|
+
Steep.logger.formatter.push_tags(*tags, "from-worker@signature")
|
140
|
+
signature_worker.reader.read do |message|
|
141
|
+
process_message_from_worker(message, worker: signature_worker)
|
142
|
+
end
|
52
143
|
end
|
53
|
-
end
|
54
144
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
145
|
+
code_workers.each do |worker|
|
146
|
+
worker_threads << Thread.new do
|
147
|
+
Steep.logger.formatter.push_tags(*tags, "from-worker@#{worker.name}")
|
148
|
+
worker.reader.read do |message|
|
149
|
+
process_message_from_worker(message, worker: worker)
|
150
|
+
end
|
60
151
|
end
|
61
152
|
end
|
62
|
-
end
|
63
153
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
154
|
+
worker_threads << Thread.new do
|
155
|
+
Steep.logger.formatter.push_tags(*tags, "write")
|
156
|
+
while message = write_queue.pop
|
157
|
+
writer.write(message)
|
158
|
+
end
|
159
|
+
|
160
|
+
writer.io.close
|
68
161
|
end
|
69
|
-
end
|
70
162
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
163
|
+
worker_threads << Thread.new do
|
164
|
+
Steep.logger.formatter.push_tags(*tags, "reconciliation")
|
165
|
+
while (message, worker = recon_queue.pop)
|
166
|
+
id = message[:id]
|
167
|
+
handler = response_handlers[id] or raise
|
168
|
+
|
169
|
+
Steep.logger.info "Processing response to #{handler.request[:method]}(#{id}) from #{worker.name}"
|
170
|
+
|
171
|
+
handler.process_response(message, worker)
|
172
|
+
|
173
|
+
if handler.completed?
|
174
|
+
Steep.logger.info "Response to #{handler.request[:method]}(#{id}) completed"
|
175
|
+
response_handlers.delete(id)
|
176
|
+
end
|
76
177
|
end
|
77
|
-
else
|
78
|
-
writer.write(job)
|
79
178
|
end
|
80
|
-
end
|
81
179
|
|
82
|
-
|
180
|
+
Steep.logger.tagged "main" do
|
181
|
+
reader.read do |request|
|
182
|
+
process_message_from_client(request) or break
|
183
|
+
end
|
83
184
|
|
84
|
-
|
85
|
-
|
185
|
+
worker_threads.each do |thread|
|
186
|
+
thread.join
|
187
|
+
end
|
188
|
+
end
|
86
189
|
end
|
87
190
|
end
|
88
191
|
|
@@ -97,28 +200,29 @@ module Steep
|
|
97
200
|
end
|
98
201
|
|
99
202
|
def process_message_from_client(message)
|
203
|
+
Steep.logger.info "Received message #{message[:method]}(#{message[:id]})"
|
100
204
|
id = message[:id]
|
101
205
|
|
102
206
|
case message[:method]
|
103
207
|
when "initialize"
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
208
|
+
broadcast_request(message) do |handler|
|
209
|
+
handler.on_completion do
|
210
|
+
write_queue << {
|
211
|
+
id: id,
|
212
|
+
result: LSP::Interface::InitializeResult.new(
|
213
|
+
capabilities: LSP::Interface::ServerCapabilities.new(
|
214
|
+
text_document_sync: LSP::Interface::TextDocumentSyncOptions.new(
|
215
|
+
change: LSP::Constant::TextDocumentSyncKind::INCREMENTAL
|
216
|
+
),
|
217
|
+
hover_provider: true,
|
218
|
+
completion_provider: LSP::Interface::CompletionOptions.new(
|
219
|
+
trigger_characters: [".", "@"]
|
220
|
+
),
|
221
|
+
workspace_symbol_provider: true
|
222
|
+
)
|
223
|
+
)
|
224
|
+
}
|
225
|
+
end
|
122
226
|
end
|
123
227
|
|
124
228
|
when "textDocument/didChange"
|
@@ -131,33 +235,106 @@ module Steep
|
|
131
235
|
register_code_to_worker [path], worker: least_busy_worker()
|
132
236
|
end
|
133
237
|
|
134
|
-
|
135
|
-
worker << message
|
136
|
-
end
|
137
|
-
|
138
|
-
when "textDocument/hover"
|
139
|
-
interaction_worker << message
|
238
|
+
broadcast_notification(message)
|
140
239
|
|
141
|
-
when "textDocument/completion"
|
142
|
-
interaction_worker
|
240
|
+
when "textDocument/hover", "textDocument/completion"
|
241
|
+
send_request(message, worker: interaction_worker) do |handler|
|
242
|
+
handler.on_completion do |response|
|
243
|
+
write_queue << response
|
244
|
+
end
|
245
|
+
end
|
143
246
|
|
144
247
|
when "textDocument/open"
|
145
248
|
# Ignores open notification
|
146
249
|
|
147
250
|
when "workspace/symbol"
|
148
|
-
signature_worker
|
251
|
+
send_request(message, worker: signature_worker) do |handler|
|
252
|
+
handler.on_completion do |response|
|
253
|
+
write_queue << response
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
when "workspace/executeCommand"
|
258
|
+
case message[:params][:command]
|
259
|
+
when "steep/stats"
|
260
|
+
send_request(message, workers: code_workers) do |handler|
|
261
|
+
handler.on_completion do |*responses|
|
262
|
+
stats = responses.flat_map {|resp| resp[:result] }
|
263
|
+
|
264
|
+
write_queue << {
|
265
|
+
id: handler.request_id,
|
266
|
+
result: stats
|
267
|
+
}
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
149
271
|
|
150
272
|
when "shutdown"
|
151
|
-
|
152
|
-
|
273
|
+
broadcast_request(message) do |handler|
|
274
|
+
handler.on_completion do |*_|
|
275
|
+
write_queue << { id: id, result: nil}
|
276
|
+
|
277
|
+
write_queue.close
|
278
|
+
recon_queue.close
|
279
|
+
end
|
280
|
+
end
|
153
281
|
|
154
282
|
when "exit"
|
155
|
-
|
283
|
+
broadcast_notification(message)
|
284
|
+
|
285
|
+
return false
|
156
286
|
end
|
287
|
+
|
288
|
+
true
|
157
289
|
end
|
158
290
|
|
159
|
-
def
|
160
|
-
|
291
|
+
def broadcast_notification(message)
|
292
|
+
Steep.logger.info "Broadcasting notification #{message[:method]}"
|
293
|
+
each_worker do |worker|
|
294
|
+
worker << message
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def send_notification(message, worker:)
|
299
|
+
Steep.logger.info "Sending notification #{message[:method]} to #{worker.name}"
|
300
|
+
worker << message
|
301
|
+
end
|
302
|
+
|
303
|
+
def send_request(message, worker: nil, workers: [])
|
304
|
+
workers << worker if worker
|
305
|
+
|
306
|
+
Steep.logger.info "Sending request #{message[:method]}(#{message[:id]}) to #{workers.map(&:name).join(", ")}"
|
307
|
+
handler = ResponseHandler.new(request: message, workers: workers)
|
308
|
+
yield(handler) if block_given?
|
309
|
+
response_handlers[handler.request_id] = handler
|
310
|
+
|
311
|
+
workers.each do |w|
|
312
|
+
w << message
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def broadcast_request(message)
|
317
|
+
Steep.logger.info "Broadcasting request #{message[:method]}(#{message[:id]})"
|
318
|
+
handler = ResponseHandler.new(request: message, workers: each_worker.to_a)
|
319
|
+
yield(handler) if block_given?
|
320
|
+
response_handlers[handler.request_id] = handler
|
321
|
+
|
322
|
+
each_worker do |worker|
|
323
|
+
worker << message
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def process_message_from_worker(message, worker:)
|
328
|
+
case
|
329
|
+
when message.key?(:id) && !message.key?(:method)
|
330
|
+
# Response from worker
|
331
|
+
Steep.logger.info "Received response #{message[:id]} from worker"
|
332
|
+
recon_queue << [message, worker]
|
333
|
+
when message.key?(:method) && !message.key?(:id)
|
334
|
+
# Notification from worker
|
335
|
+
Steep.logger.info "Received notification #{message[:method]} from worker"
|
336
|
+
write_queue << message
|
337
|
+
end
|
161
338
|
end
|
162
339
|
|
163
340
|
def paths_for(worker)
|
@@ -177,15 +354,24 @@ module Steep
|
|
177
354
|
def register_code_to_worker(paths, worker:)
|
178
355
|
paths_for(worker).merge(paths)
|
179
356
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
357
|
+
send_notification(
|
358
|
+
{
|
359
|
+
method: "workspace/executeCommand",
|
360
|
+
params: LSP::Interface::ExecuteCommandParams.new(
|
361
|
+
command: "steep/registerSourceToWorker",
|
362
|
+
arguments: paths.map do |path|
|
363
|
+
"file://#{project.absolute_path(path)}"
|
364
|
+
end
|
365
|
+
)
|
366
|
+
},
|
367
|
+
worker: worker
|
368
|
+
)
|
369
|
+
end
|
370
|
+
|
371
|
+
def kill
|
372
|
+
each_worker do |worker|
|
373
|
+
worker.kill
|
374
|
+
end
|
189
375
|
end
|
190
376
|
end
|
191
377
|
end
|