steep-relaxed 1.9.3.3
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 +7 -0
- data/.gitignore +13 -0
- data/.gitmodules +0 -0
- data/CHANGELOG.md +1032 -0
- data/LICENSE +21 -0
- data/README.md +260 -0
- data/Rakefile +227 -0
- data/STDGEM_DEPENDENCIES.txt +59 -0
- data/Steepfile +68 -0
- data/bin/console +14 -0
- data/bin/generate-diagnostics-docs.rb +112 -0
- data/bin/mem_graph.rb +67 -0
- data/bin/mem_prof.rb +102 -0
- data/bin/output_rebaseline.rb +34 -0
- data/bin/output_test.rb +60 -0
- data/bin/rbs +20 -0
- data/bin/rbs-inline +19 -0
- data/bin/setup +9 -0
- data/bin/stackprof_test.rb +19 -0
- data/bin/steep +19 -0
- data/bin/steep-check.rb +251 -0
- data/bin/steep-prof +16 -0
- data/doc/narrowing.md +195 -0
- data/doc/shape.md +194 -0
- data/exe/steep +18 -0
- data/guides/README.md +5 -0
- data/guides/src/gem-rbs-collection/gem-rbs-collection.md +126 -0
- data/guides/src/getting-started/getting-started.md +163 -0
- data/guides/src/nil-optional/nil-optional.md +195 -0
- data/lib/steep/annotation_parser.rb +199 -0
- data/lib/steep/ast/annotation/collection.rb +172 -0
- data/lib/steep/ast/annotation.rb +137 -0
- data/lib/steep/ast/builtin.rb +104 -0
- data/lib/steep/ast/ignore.rb +148 -0
- data/lib/steep/ast/node/type_application.rb +88 -0
- data/lib/steep/ast/node/type_assertion.rb +81 -0
- data/lib/steep/ast/types/any.rb +35 -0
- data/lib/steep/ast/types/boolean.rb +45 -0
- data/lib/steep/ast/types/bot.rb +35 -0
- data/lib/steep/ast/types/class.rb +43 -0
- data/lib/steep/ast/types/factory.rb +557 -0
- data/lib/steep/ast/types/helper.rb +40 -0
- data/lib/steep/ast/types/instance.rb +42 -0
- data/lib/steep/ast/types/intersection.rb +93 -0
- data/lib/steep/ast/types/literal.rb +59 -0
- data/lib/steep/ast/types/logic.rb +84 -0
- data/lib/steep/ast/types/name.rb +128 -0
- data/lib/steep/ast/types/nil.rb +41 -0
- data/lib/steep/ast/types/proc.rb +117 -0
- data/lib/steep/ast/types/record.rb +79 -0
- data/lib/steep/ast/types/self.rb +43 -0
- data/lib/steep/ast/types/shared_instance.rb +11 -0
- data/lib/steep/ast/types/top.rb +35 -0
- data/lib/steep/ast/types/tuple.rb +60 -0
- data/lib/steep/ast/types/union.rb +97 -0
- data/lib/steep/ast/types/var.rb +65 -0
- data/lib/steep/ast/types/void.rb +35 -0
- data/lib/steep/cli.rb +401 -0
- data/lib/steep/diagnostic/deprecated/else_on_exhaustive_case.rb +20 -0
- data/lib/steep/diagnostic/deprecated/unknown_constant_assigned.rb +28 -0
- data/lib/steep/diagnostic/helper.rb +18 -0
- data/lib/steep/diagnostic/lsp_formatter.rb +78 -0
- data/lib/steep/diagnostic/result_printer2.rb +48 -0
- data/lib/steep/diagnostic/ruby.rb +1221 -0
- data/lib/steep/diagnostic/signature.rb +570 -0
- data/lib/steep/drivers/annotations.rb +52 -0
- data/lib/steep/drivers/check.rb +339 -0
- data/lib/steep/drivers/checkfile.rb +210 -0
- data/lib/steep/drivers/diagnostic_printer.rb +105 -0
- data/lib/steep/drivers/init.rb +66 -0
- data/lib/steep/drivers/langserver.rb +56 -0
- data/lib/steep/drivers/print_project.rb +113 -0
- data/lib/steep/drivers/stats.rb +203 -0
- data/lib/steep/drivers/utils/driver_helper.rb +143 -0
- data/lib/steep/drivers/utils/jobs_option.rb +26 -0
- data/lib/steep/drivers/vendor.rb +27 -0
- data/lib/steep/drivers/watch.rb +194 -0
- data/lib/steep/drivers/worker.rb +58 -0
- data/lib/steep/equatable.rb +23 -0
- data/lib/steep/expectations.rb +228 -0
- data/lib/steep/index/rbs_index.rb +350 -0
- data/lib/steep/index/signature_symbol_provider.rb +185 -0
- data/lib/steep/index/source_index.rb +167 -0
- data/lib/steep/interface/block.rb +103 -0
- data/lib/steep/interface/builder.rb +843 -0
- data/lib/steep/interface/function.rb +1090 -0
- data/lib/steep/interface/method_type.rb +330 -0
- data/lib/steep/interface/shape.rb +239 -0
- data/lib/steep/interface/substitution.rb +159 -0
- data/lib/steep/interface/type_param.rb +115 -0
- data/lib/steep/located_value.rb +20 -0
- data/lib/steep/method_name.rb +42 -0
- data/lib/steep/module_helper.rb +24 -0
- data/lib/steep/node_helper.rb +273 -0
- data/lib/steep/path_helper.rb +30 -0
- data/lib/steep/project/dsl.rb +268 -0
- data/lib/steep/project/group.rb +31 -0
- data/lib/steep/project/options.rb +63 -0
- data/lib/steep/project/pattern.rb +59 -0
- data/lib/steep/project/target.rb +92 -0
- data/lib/steep/project.rb +78 -0
- data/lib/steep/rake_task.rb +132 -0
- data/lib/steep/range_extension.rb +29 -0
- data/lib/steep/server/base_worker.rb +97 -0
- data/lib/steep/server/change_buffer.rb +73 -0
- data/lib/steep/server/custom_methods.rb +77 -0
- data/lib/steep/server/delay_queue.rb +45 -0
- data/lib/steep/server/interaction_worker.rb +492 -0
- data/lib/steep/server/lsp_formatter.rb +455 -0
- data/lib/steep/server/master.rb +922 -0
- data/lib/steep/server/target_group_files.rb +205 -0
- data/lib/steep/server/type_check_controller.rb +366 -0
- data/lib/steep/server/type_check_worker.rb +303 -0
- data/lib/steep/server/work_done_progress.rb +64 -0
- data/lib/steep/server/worker_process.rb +176 -0
- data/lib/steep/services/completion_provider.rb +802 -0
- data/lib/steep/services/content_change.rb +61 -0
- data/lib/steep/services/file_loader.rb +74 -0
- data/lib/steep/services/goto_service.rb +441 -0
- data/lib/steep/services/hover_provider/rbs.rb +88 -0
- data/lib/steep/services/hover_provider/ruby.rb +221 -0
- data/lib/steep/services/hover_provider/singleton_methods.rb +20 -0
- data/lib/steep/services/path_assignment.rb +46 -0
- data/lib/steep/services/signature_help_provider.rb +202 -0
- data/lib/steep/services/signature_service.rb +428 -0
- data/lib/steep/services/stats_calculator.rb +68 -0
- data/lib/steep/services/type_check_service.rb +394 -0
- data/lib/steep/services/type_name_completion.rb +236 -0
- data/lib/steep/signature/validator.rb +651 -0
- data/lib/steep/source/ignore_ranges.rb +69 -0
- data/lib/steep/source.rb +691 -0
- data/lib/steep/subtyping/cache.rb +30 -0
- data/lib/steep/subtyping/check.rb +1113 -0
- data/lib/steep/subtyping/constraints.rb +341 -0
- data/lib/steep/subtyping/relation.rb +101 -0
- data/lib/steep/subtyping/result.rb +324 -0
- data/lib/steep/subtyping/variable_variance.rb +89 -0
- data/lib/steep/test.rb +9 -0
- data/lib/steep/thread_waiter.rb +43 -0
- data/lib/steep/type_construction.rb +5183 -0
- data/lib/steep/type_inference/block_params.rb +416 -0
- data/lib/steep/type_inference/case_when.rb +303 -0
- data/lib/steep/type_inference/constant_env.rb +56 -0
- data/lib/steep/type_inference/context.rb +195 -0
- data/lib/steep/type_inference/logic_type_interpreter.rb +613 -0
- data/lib/steep/type_inference/method_call.rb +193 -0
- data/lib/steep/type_inference/method_params.rb +531 -0
- data/lib/steep/type_inference/multiple_assignment.rb +194 -0
- data/lib/steep/type_inference/send_args.rb +712 -0
- data/lib/steep/type_inference/type_env.rb +341 -0
- data/lib/steep/type_inference/type_env_builder.rb +138 -0
- data/lib/steep/typing.rb +321 -0
- data/lib/steep/version.rb +3 -0
- data/lib/steep.rb +369 -0
- data/manual/annotations.md +181 -0
- data/manual/ignore.md +20 -0
- data/manual/ruby-diagnostics.md +1879 -0
- data/sample/Steepfile +22 -0
- data/sample/lib/conference.rb +49 -0
- data/sample/lib/length.rb +35 -0
- data/sample/sig/conference.rbs +42 -0
- data/sample/sig/generics.rbs +15 -0
- data/sample/sig/length.rbs +34 -0
- data/steep-relaxed.gemspec +56 -0
- metadata +340 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
require "rake"
|
2
|
+
require "rake/tasklib"
|
3
|
+
require "steep/cli"
|
4
|
+
|
5
|
+
module Steep
|
6
|
+
# Provides Rake tasks for running Steep commands.
|
7
|
+
#
|
8
|
+
# require "steep/rake_task"
|
9
|
+
# Steep::RakeTask.new do |t|
|
10
|
+
# t.check.severity_level = :error
|
11
|
+
# t.watch.verbose
|
12
|
+
# t.stats << "--format=table"
|
13
|
+
# end
|
14
|
+
class RakeTask < Rake::TaskLib # steep:ignore UnknownConstant
|
15
|
+
attr_accessor :name
|
16
|
+
|
17
|
+
def self.available_commands
|
18
|
+
%i(init check stats binstub project watch)
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(name = :steep, cli_runner = default_cli_runner)
|
22
|
+
super()
|
23
|
+
|
24
|
+
@name = name
|
25
|
+
|
26
|
+
configuration = TaskConfiguration.new
|
27
|
+
|
28
|
+
yield configuration if block_given?
|
29
|
+
|
30
|
+
define_tasks(configuration, cli_runner)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# :nodoc:
|
36
|
+
class TaskConfiguration
|
37
|
+
def initialize
|
38
|
+
@commands = {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def method_missing(command)
|
42
|
+
if respond_to?(command)
|
43
|
+
@commands[command] ||= CommandConfiguration.new
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def respond_to_missing?(name, include_private = false)
|
50
|
+
RakeTask.available_commands.include?(name) || super
|
51
|
+
end
|
52
|
+
|
53
|
+
def options(command)
|
54
|
+
@commands[command]&.to_a || []
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# :nodoc:
|
59
|
+
class CommandConfiguration
|
60
|
+
def initialize
|
61
|
+
@options = []
|
62
|
+
end
|
63
|
+
|
64
|
+
def method_missing(name, value = nil)
|
65
|
+
option = "--#{name.to_s.gsub(/_/, '-').gsub(/=/, '')}"
|
66
|
+
option << "=#{value}" if value
|
67
|
+
|
68
|
+
self << option
|
69
|
+
end
|
70
|
+
|
71
|
+
def respond_to_missing?(_name)
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
def <<(value)
|
76
|
+
@options << value
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_a
|
81
|
+
@options
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def default_cli_runner
|
86
|
+
lambda do |arguments|
|
87
|
+
require "steep"
|
88
|
+
|
89
|
+
cli = Steep::CLI.new(
|
90
|
+
stdout: $stdout,
|
91
|
+
stdin: $stdin,
|
92
|
+
stderr: $stderr,
|
93
|
+
argv: arguments
|
94
|
+
)
|
95
|
+
|
96
|
+
cli.run
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def define_tasks(configuration, cli_runner)
|
101
|
+
namespace name do
|
102
|
+
RakeTask.available_commands.each do |command|
|
103
|
+
desc "Run steep #{command}"
|
104
|
+
task command do |_, args|
|
105
|
+
configured_options = configuration.options(command)
|
106
|
+
|
107
|
+
argv = [
|
108
|
+
command.to_s,
|
109
|
+
*configured_options,
|
110
|
+
*args.extras
|
111
|
+
]
|
112
|
+
|
113
|
+
result = cli_runner[argv]
|
114
|
+
|
115
|
+
raise "Steep failed" if result.nonzero?
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
desc "Run steep help"
|
120
|
+
task "help" do
|
121
|
+
arguments = ["--help"]
|
122
|
+
|
123
|
+
cli_runner[arguments]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Default steep task to steep:check
|
128
|
+
desc "Run steep check" unless ::Rake.application.last_description # steep:ignore UnknownConstant
|
129
|
+
task name => ["#{name}:check"]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -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
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Steep
|
2
|
+
module Server
|
3
|
+
class BaseWorker
|
4
|
+
LSP = LanguageServer::Protocol
|
5
|
+
|
6
|
+
attr_reader :project
|
7
|
+
attr_reader :reader, :writer, :queue
|
8
|
+
|
9
|
+
ShutdownJob = _ = Struct.new(:id, keyword_init: true)
|
10
|
+
|
11
|
+
def initialize(project:, reader:, writer:)
|
12
|
+
@project = project
|
13
|
+
@reader = reader
|
14
|
+
@writer = writer
|
15
|
+
@skip_job = false
|
16
|
+
@shutdown = false
|
17
|
+
@skip_jobs_after_shutdown = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def skip_jobs_after_shutdown!(flag = true)
|
21
|
+
@skip_jobs_after_shutdown = flag
|
22
|
+
end
|
23
|
+
|
24
|
+
def skip_jobs_after_shutdown?
|
25
|
+
@skip_jobs_after_shutdown
|
26
|
+
end
|
27
|
+
|
28
|
+
def skip_job?
|
29
|
+
@skip_job
|
30
|
+
end
|
31
|
+
|
32
|
+
def handle_request(request)
|
33
|
+
# process request
|
34
|
+
end
|
35
|
+
|
36
|
+
def handle_job(job)
|
37
|
+
# process async job
|
38
|
+
end
|
39
|
+
|
40
|
+
def run
|
41
|
+
tags = Steep.logger.formatter.current_tags.dup
|
42
|
+
thread = Thread.new do
|
43
|
+
Thread.current.abort_on_exception = true
|
44
|
+
|
45
|
+
Steep.logger.formatter.push_tags(*tags)
|
46
|
+
Steep.logger.tagged "background" do
|
47
|
+
while job = queue.pop
|
48
|
+
case job
|
49
|
+
when ShutdownJob
|
50
|
+
writer.write(id: job.id, result: nil)
|
51
|
+
else
|
52
|
+
if skip_job?
|
53
|
+
Steep.logger.info "Skipping job..."
|
54
|
+
else
|
55
|
+
begin
|
56
|
+
handle_job(job)
|
57
|
+
rescue => exn
|
58
|
+
Steep.log_error exn
|
59
|
+
writer.write(
|
60
|
+
{
|
61
|
+
method: "window/showMessage",
|
62
|
+
params: {
|
63
|
+
type: LSP::Constant::MessageType::ERROR,
|
64
|
+
message: "Unexpected error: #{exn.message} (#{exn.class})"
|
65
|
+
}
|
66
|
+
}
|
67
|
+
)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
Steep.logger.tagged "frontend" do
|
76
|
+
begin
|
77
|
+
reader.read do |request|
|
78
|
+
Steep.logger.info "Received message from master: #{request[:method]}(#{request[:id]})"
|
79
|
+
case request[:method]
|
80
|
+
when "shutdown"
|
81
|
+
queue << ShutdownJob.new(id: request[:id])
|
82
|
+
@skip_job = skip_jobs_after_shutdown?
|
83
|
+
queue.close
|
84
|
+
when "exit"
|
85
|
+
break
|
86
|
+
else
|
87
|
+
handle_request(request) unless @shutdown
|
88
|
+
end
|
89
|
+
end
|
90
|
+
ensure
|
91
|
+
thread.join
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Steep
|
2
|
+
module Server
|
3
|
+
module ChangeBuffer
|
4
|
+
attr_reader :mutex
|
5
|
+
attr_reader :buffered_changes
|
6
|
+
|
7
|
+
def push_buffer
|
8
|
+
mutex.synchronize do
|
9
|
+
yield buffered_changes
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def pop_buffer
|
14
|
+
changes = mutex.synchronize do
|
15
|
+
copy = buffered_changes.dup
|
16
|
+
buffered_changes.clear
|
17
|
+
copy
|
18
|
+
end
|
19
|
+
|
20
|
+
if block_given?
|
21
|
+
yield changes
|
22
|
+
else
|
23
|
+
changes
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_files(input)
|
28
|
+
Steep.logger.tagged "#load_files" do
|
29
|
+
push_buffer do |changes|
|
30
|
+
input.each do |filename, content|
|
31
|
+
if content.is_a?(Hash)
|
32
|
+
content = Base64.decode64(content[:text]).force_encoding(Encoding::UTF_8)
|
33
|
+
end
|
34
|
+
changes[Pathname(filename.to_s)] = [Services::ContentChange.new(text: content)]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def collect_changes(request)
|
41
|
+
push_buffer do |changes|
|
42
|
+
if path = Steep::PathHelper.to_pathname(request[:params][:textDocument][:uri])
|
43
|
+
path = project.relative_path(path)
|
44
|
+
version = request[:params][:textDocument][:version]
|
45
|
+
Steep.logger.info { "Updating source: path=#{path}, version=#{version}..." }
|
46
|
+
|
47
|
+
changes[path] ||= []
|
48
|
+
request[:params][:contentChanges].each do |change|
|
49
|
+
changes.fetch(path) << Services::ContentChange.new(
|
50
|
+
range: change[:range]&.yield_self {|range|
|
51
|
+
[
|
52
|
+
range[:start].yield_self {|pos| Services::ContentChange::Position.new(line: pos[:line] + 1, column: pos[:character]) },
|
53
|
+
range[:end].yield_self {|pos| Services::ContentChange::Position.new(line: pos[:line] + 1, column: pos[:character]) }
|
54
|
+
]
|
55
|
+
},
|
56
|
+
text: change[:text]
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def reset_change(uri:, text:)
|
64
|
+
push_buffer do |changes|
|
65
|
+
if path = Steep::PathHelper.to_pathname(uri)
|
66
|
+
path = project.relative_path(path)
|
67
|
+
changes[path] = [Services::ContentChange.new(text: text)]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Steep
|
2
|
+
module Server
|
3
|
+
module CustomMethods
|
4
|
+
module FileLoad
|
5
|
+
METHOD = "$/steep/file/load"
|
6
|
+
|
7
|
+
def self.notification(params)
|
8
|
+
{ method: METHOD, params: params }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module FileReset
|
13
|
+
METHOD = "$/steep/file/reset"
|
14
|
+
|
15
|
+
def self.notification(params)
|
16
|
+
{ method: METHOD, params: params }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module TypeCheck
|
21
|
+
METHOD = "$/steep/typecheck"
|
22
|
+
|
23
|
+
def self.request(id, params)
|
24
|
+
{ method: METHOD, id: id, params: params }
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.response(id, result)
|
28
|
+
{ id: id, result: result }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module TypeCheckGroups
|
33
|
+
METHOD = "$/steep/typecheck/groups"
|
34
|
+
|
35
|
+
def self.notification(params)
|
36
|
+
{ method: METHOD, params: params }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module TypeCheck__Start
|
41
|
+
METHOD = "$/steep/typecheck/start"
|
42
|
+
|
43
|
+
def self.notification(params)
|
44
|
+
{ method: METHOD, params: params }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module TypeCheck__Progress
|
49
|
+
METHOD = "$/steep/typecheck/progress"
|
50
|
+
|
51
|
+
def self.notification(params)
|
52
|
+
{ method: METHOD, params: params }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module Stats
|
57
|
+
METHOD = "$/steep/stats"
|
58
|
+
|
59
|
+
def self.request(id)
|
60
|
+
{ method: METHOD, id: id, params: nil }
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.response(id, result)
|
64
|
+
{ id: id, result: result }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module Groups
|
69
|
+
METHOD = "$/steep/groups"
|
70
|
+
|
71
|
+
def self.response(id, result)
|
72
|
+
{ id: id, result: result }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Steep
|
2
|
+
module Server
|
3
|
+
class DelayQueue
|
4
|
+
attr_reader :delay, :thread, :queue, :last_task
|
5
|
+
|
6
|
+
def initialize(delay:)
|
7
|
+
@delay = delay
|
8
|
+
|
9
|
+
@queue = Thread::Queue.new
|
10
|
+
|
11
|
+
@thread = Thread.new do
|
12
|
+
while (scheduled_at, proc = queue.pop)
|
13
|
+
diff = scheduled_at - Time.now
|
14
|
+
case
|
15
|
+
when diff > 0.1
|
16
|
+
sleep diff
|
17
|
+
when diff > 0
|
18
|
+
while Time.now < scheduled_at
|
19
|
+
# nop
|
20
|
+
sleep 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
if proc.equal?(last_task)
|
25
|
+
unless @cancelled
|
26
|
+
proc[]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def cancel
|
34
|
+
@cancelled = true
|
35
|
+
queue.clear()
|
36
|
+
end
|
37
|
+
|
38
|
+
def execute(&block)
|
39
|
+
@last_task = block
|
40
|
+
scheduled_at = Time.now + delay
|
41
|
+
queue << [scheduled_at, block]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|