thread_weaver 0.1.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 +7 -0
- data/.github/workflows/main.yml +19 -0
- data/.gitignore +59 -0
- data/.rspec +3 -0
- data/.vscode/settings.json +14 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +17 -0
- data/LICENSE +21 -0
- data/README.md +37 -0
- data/Rakefile +13 -0
- data/bin/console +15 -0
- data/bin/gem_smoke_test +10 -0
- data/bin/setup +8 -0
- data/examples/always_deadlocks.rb +15 -0
- data/examples/takes_a_while.rb +7 -0
- data/examples/thread_safe_nonblocking_run_at_most_once.rb +23 -0
- data/examples/thread_safe_run_at_most_once.rb +19 -0
- data/examples/thread_unsafe_run_at_most_once.rb +17 -0
- data/lib/thread_weaver.rb +15 -0
- data/lib/thread_weaver/controllable_thread.rb +177 -0
- data/lib/thread_weaver/iterative_race_detector.rb +178 -0
- data/lib/thread_weaver/thread_instruction.rb +68 -0
- data/lib/thread_weaver/version.rb +6 -0
- data/sorbet/config +2 -0
- data/sorbet/rbi/gems/ast.rbi +48 -0
- data/sorbet/rbi/gems/coderay.rbi +285 -0
- data/sorbet/rbi/gems/method_source.rbi +64 -0
- data/sorbet/rbi/gems/parallel.rbi +83 -0
- data/sorbet/rbi/gems/parser.rbi +1510 -0
- data/sorbet/rbi/gems/pry-nav.rbi +29 -0
- data/sorbet/rbi/gems/pry.rbi +1965 -0
- data/sorbet/rbi/gems/rainbow.rbi +118 -0
- data/sorbet/rbi/gems/rake.rbi +645 -0
- data/sorbet/rbi/gems/regexp_parser.rbi +920 -0
- data/sorbet/rbi/gems/rexml.rbi +589 -0
- data/sorbet/rbi/gems/rspec-core.rbi +1893 -0
- data/sorbet/rbi/gems/rspec-expectations.rbi +1148 -0
- data/sorbet/rbi/gems/rspec-mocks.rbi +1091 -0
- data/sorbet/rbi/gems/rspec-support.rbi +280 -0
- data/sorbet/rbi/gems/rspec.rbi +15 -0
- data/sorbet/rbi/gems/rubocop-ast.rbi +1351 -0
- data/sorbet/rbi/gems/rubocop-performance.rbi +471 -0
- data/sorbet/rbi/gems/rubocop.rbi +7510 -0
- data/sorbet/rbi/gems/ruby-progressbar.rbi +305 -0
- data/sorbet/rbi/gems/standard.rbi +141 -0
- data/sorbet/rbi/gems/unicode-display_width.rbi +17 -0
- data/sorbet/rbi/hidden-definitions/errors.txt +4309 -0
- data/sorbet/rbi/hidden-definitions/hidden.rbi +8622 -0
- data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +276 -0
- data/sorbet/rbi/sorbet-typed/lib/rubocop-performance/~>1.6/rubocop-performance.rbi +149 -0
- data/sorbet/rbi/todo.rbi +6 -0
- data/thread_weaver.gemspec +34 -0
- metadata +111 -0
@@ -0,0 +1,178 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module ThreadWeaver
|
4
|
+
class RaceConditionDetectedError < Error; end
|
5
|
+
|
6
|
+
class DeadlockDetectedError < Error; end
|
7
|
+
|
8
|
+
class BlockingSynchronizationDetected < Error; end
|
9
|
+
|
10
|
+
DEADLOCK_MIGHT_BE_CONFIG = T.let(
|
11
|
+
"Either there is a deadlock, or assume_deadlocked_after_ms is set too low. Try increasing "\
|
12
|
+
"assume_deadlocked_after_ms to a higher value.",
|
13
|
+
String
|
14
|
+
)
|
15
|
+
|
16
|
+
class IterativeRaceDetector
|
17
|
+
extend T::Sig
|
18
|
+
|
19
|
+
sig do
|
20
|
+
params(
|
21
|
+
setup: T.proc.returns(T.untyped),
|
22
|
+
run: T.proc.params(arg0: T.untyped).void,
|
23
|
+
check: T.proc.params(arg0: T.untyped).returns(T::Boolean),
|
24
|
+
target_classes: T::Array[Module],
|
25
|
+
assume_deadlocked_after_ms: Integer,
|
26
|
+
run_secondary: T.nilable(T.proc.params(arg0: T.untyped).void),
|
27
|
+
expect_nonblocking: T::Boolean
|
28
|
+
).void
|
29
|
+
end
|
30
|
+
def initialize(setup:, run:, check:, target_classes:, assume_deadlocked_after_ms:, run_secondary: nil, expect_nonblocking: false)
|
31
|
+
@setup_blk = T.let(setup, T.proc.returns(T.untyped))
|
32
|
+
@check_blk = T.let(check, T.proc.params(arg0: T.untyped).returns(T::Boolean))
|
33
|
+
@target_classes = T.let(target_classes, T::Array[Module])
|
34
|
+
@expect_nonblocking = T.let(expect_nonblocking, T::Boolean)
|
35
|
+
|
36
|
+
@run_blk = T.let(run, T.proc.params(arg0: T.untyped).void)
|
37
|
+
# Secondary is optional as the common case will be testing two identical blocks of code
|
38
|
+
@run_secondary_blk = T.let(run_secondary || run, T.proc.params(arg0: T.untyped).void)
|
39
|
+
|
40
|
+
@assume_deadlocked_after = T.let(assume_deadlocked_after_ms / 1000.0, Float)
|
41
|
+
|
42
|
+
@scenarios_run = T.let(0, Integer)
|
43
|
+
@secondary_deadlocked_count = T.let(0, Integer)
|
44
|
+
end
|
45
|
+
|
46
|
+
sig { void }
|
47
|
+
def run
|
48
|
+
check_if_can_run_standalone(@run_blk, "run")
|
49
|
+
if @run_secondary_blk != @run_blk
|
50
|
+
check_if_can_run_standalone(@run_secondary_blk, "run_secondary")
|
51
|
+
end
|
52
|
+
|
53
|
+
hold_primary_at_line_count = -1
|
54
|
+
done = T.let(false, T::Boolean)
|
55
|
+
|
56
|
+
error_encountered = T.let(nil, T.nilable(Exception))
|
57
|
+
|
58
|
+
until done
|
59
|
+
Timeout.timeout(2 * @assume_deadlocked_after) do
|
60
|
+
hold_primary_at_line_count += 1
|
61
|
+
|
62
|
+
context = @setup_blk.call
|
63
|
+
|
64
|
+
primary_thread = ControllableThread.new(context, name: "primary_thread", &@run_blk)
|
65
|
+
|
66
|
+
# Pause the primary thread after it executes hold_primary_at_line_count number of lines
|
67
|
+
primary_thread.set_and_wait_for_next_instruction(
|
68
|
+
PauseWhenLineCount.new(
|
69
|
+
count: hold_primary_at_line_count,
|
70
|
+
target_classes: @target_classes
|
71
|
+
)
|
72
|
+
)
|
73
|
+
|
74
|
+
# Start secondary thread and instruct it to run until completion. The primary thread is
|
75
|
+
# still paused part-way through its execution
|
76
|
+
secondary_thread = ControllableThread.new(context, name: "secondary_thread", &@run_secondary_blk)
|
77
|
+
secondary_thread.set_next_instruction(ContinueToThreadEnd.new)
|
78
|
+
|
79
|
+
if check_for_deadlock(secondary_thread)
|
80
|
+
# At this point, it appears that the secondary thread is deadlocked. This could be
|
81
|
+
# because of a true deadlock, but this also is expected to happen even in thread-safe
|
82
|
+
# code that uses blocking locks. If blocking locks are used then its quite likely that
|
83
|
+
# pausing the primary thread at certain points might would block the secondary thread.
|
84
|
+
# For that reason, this isn't considered an outright error.
|
85
|
+
@secondary_deadlocked_count += 1
|
86
|
+
primary_thread.set_and_wait_for_next_instruction(ContinueToThreadEnd.new)
|
87
|
+
|
88
|
+
if @expect_nonblocking
|
89
|
+
error_encountered ||= BlockingSynchronizationDetected.new(
|
90
|
+
"Deadlock detected, but expect_nonblocking was set to true. Make sure you aren't "\
|
91
|
+
"blocking waiting for a lock."
|
92
|
+
)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Wait for the secondary thread to complete, taking note of any errors
|
97
|
+
begin
|
98
|
+
secondary_thread.join
|
99
|
+
rescue => e
|
100
|
+
# Defer exception until after the primary thread gets a chance to join, to avoid leaking
|
101
|
+
# threads
|
102
|
+
error_encountered ||= e
|
103
|
+
end
|
104
|
+
|
105
|
+
# Only now that the secondary thread has completed, release the primary thread
|
106
|
+
primary_thread.set_and_wait_for_next_instruction(ContinueToThreadEnd.new)
|
107
|
+
|
108
|
+
begin
|
109
|
+
primary_thread.join
|
110
|
+
@scenarios_run += 1
|
111
|
+
rescue ThreadCompletedEarlyError
|
112
|
+
# ThreadCompletedEarlyError will occur if the primary thread never successfully paused
|
113
|
+
# at the specified location. This happens normally, when hold_primary_at_line_count is
|
114
|
+
# incremented until it exceeds the number of lines actually executed in the primary
|
115
|
+
# thread. Once that point is reached, the primary thread will never get caught on the
|
116
|
+
# PauseWhenLineCount instruction. ControllableThread signals failures to execute a given
|
117
|
+
# instruction by returning a ThreadCompletedEarlyError, which we use as a signal to stop
|
118
|
+
# probing for race conditions. When this happens, we have done a race condition check
|
119
|
+
# with the primary thread paused at every possible location in target_classes, assuming
|
120
|
+
# the code in question is deterministic, so there is nothing left to check.
|
121
|
+
done = true
|
122
|
+
end
|
123
|
+
|
124
|
+
# Now that both threads have had a chance to join, raise any errors discovered
|
125
|
+
raise error_encountered if error_encountered
|
126
|
+
|
127
|
+
check_passed = @check_blk.call(context)
|
128
|
+
|
129
|
+
unless check_passed
|
130
|
+
raise RaceConditionDetectedError.new("Test failed")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
if @secondary_deadlocked_count == @scenarios_run
|
136
|
+
message = "In every scenario, the secondary thread was assumed deadlocked while the "\
|
137
|
+
"primary thread was paused. #{DEADLOCK_MIGHT_BE_CONFIG}"
|
138
|
+
raise DeadlockDetectedError.new(message)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
sig { params(blk: T.proc.params(arg0: T.untyped).void, name: String).void }
|
143
|
+
def check_if_can_run_standalone(blk, name)
|
144
|
+
context = @setup_blk.call
|
145
|
+
thread = ControllableThread.new(context, name: name, &blk)
|
146
|
+
thread.set_next_instruction(ContinueToThreadEnd.new)
|
147
|
+
|
148
|
+
if check_for_deadlock(thread)
|
149
|
+
thread.kill
|
150
|
+
message = "#{name} appears to be deadlocked when running alone, with no other concurrent "\
|
151
|
+
"threads. #{DEADLOCK_MIGHT_BE_CONFIG}"
|
152
|
+
|
153
|
+
raise DeadlockDetectedError.new(message)
|
154
|
+
end
|
155
|
+
|
156
|
+
check_passed = @check_blk.call(context)
|
157
|
+
|
158
|
+
unless check_passed
|
159
|
+
message = "#{name} failed check when running alone, with no other concurrent threads. "\
|
160
|
+
"Your check logic may be flawed."
|
161
|
+
raise RaceConditionDetectedError.new(message)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
sig { params(thread: ControllableThread).returns(T::Boolean) }
|
168
|
+
def check_for_deadlock(thread)
|
169
|
+
started_at = Time.now
|
170
|
+
|
171
|
+
while thread.alive? && (Time.now - started_at) < @assume_deadlocked_after
|
172
|
+
Thread.pass
|
173
|
+
end
|
174
|
+
|
175
|
+
thread.alive?
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module ThreadWeaver
|
4
|
+
module ThreadInstruction
|
5
|
+
extend T::Sig
|
6
|
+
extend T::Helpers
|
7
|
+
interface!
|
8
|
+
sealed!
|
9
|
+
|
10
|
+
sig { abstract.params(m: Module).returns(T::Boolean) }
|
11
|
+
def is_a?(m)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class PauseAtThreadStart < T::Struct
|
16
|
+
include ThreadInstruction
|
17
|
+
end
|
18
|
+
|
19
|
+
class ContinueToThreadEnd < T::Struct
|
20
|
+
include ThreadInstruction
|
21
|
+
end
|
22
|
+
|
23
|
+
class PauseWhenLineCount < T::Struct
|
24
|
+
include ThreadInstruction
|
25
|
+
extend T::Sig
|
26
|
+
|
27
|
+
const :count, Integer
|
28
|
+
const :target_classes, T::Array[Module]
|
29
|
+
|
30
|
+
sig { returns(PauseWhenLineCount) }
|
31
|
+
def next
|
32
|
+
PauseWhenLineCount.new(
|
33
|
+
count: count + 1,
|
34
|
+
target_classes: target_classes
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class PauseAtMethodCall < T::Struct
|
40
|
+
include ThreadInstruction
|
41
|
+
|
42
|
+
const :klass, Class
|
43
|
+
const :method_name, Symbol
|
44
|
+
end
|
45
|
+
|
46
|
+
class PauseAtMethodReturn < T::Struct
|
47
|
+
include ThreadInstruction
|
48
|
+
|
49
|
+
const :klass, Class
|
50
|
+
const :method_name, Symbol
|
51
|
+
end
|
52
|
+
|
53
|
+
class PauseAtSourceLine < T::Struct
|
54
|
+
include ThreadInstruction
|
55
|
+
extend T::Sig
|
56
|
+
|
57
|
+
const :path_suffix, String
|
58
|
+
const :line, Integer
|
59
|
+
|
60
|
+
sig { returns(PauseAtSourceLine) }
|
61
|
+
def next
|
62
|
+
PauseAtSourceLine.new(
|
63
|
+
line: line + 1,
|
64
|
+
path_suffix: path_suffix
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/sorbet/config
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# This file is autogenerated. Do not edit it by hand. Regenerate it with:
|
2
|
+
# srb rbi gems
|
3
|
+
|
4
|
+
# typed: strict
|
5
|
+
#
|
6
|
+
# If you would like to make changes to this file, great! Please create the gem's shim here:
|
7
|
+
#
|
8
|
+
# https://github.com/sorbet/sorbet-typed/new/master?filename=lib/ast/all/ast.rbi
|
9
|
+
#
|
10
|
+
# ast-2.4.1
|
11
|
+
|
12
|
+
module AST
|
13
|
+
end
|
14
|
+
class AST::Node
|
15
|
+
def +(array); end
|
16
|
+
def <<(element); end
|
17
|
+
def ==(other); end
|
18
|
+
def append(element); end
|
19
|
+
def assign_properties(properties); end
|
20
|
+
def children; end
|
21
|
+
def clone; end
|
22
|
+
def concat(array); end
|
23
|
+
def dup; end
|
24
|
+
def eql?(other); end
|
25
|
+
def fancy_type; end
|
26
|
+
def hash; end
|
27
|
+
def initialize(type, children = nil, properties = nil); end
|
28
|
+
def inspect(indent = nil); end
|
29
|
+
def original_dup; end
|
30
|
+
def to_a; end
|
31
|
+
def to_ast; end
|
32
|
+
def to_s(indent = nil); end
|
33
|
+
def to_sexp(indent = nil); end
|
34
|
+
def to_sexp_array; end
|
35
|
+
def type; end
|
36
|
+
def updated(type = nil, children = nil, properties = nil); end
|
37
|
+
end
|
38
|
+
class AST::Processor
|
39
|
+
include AST::Processor::Mixin
|
40
|
+
end
|
41
|
+
module AST::Processor::Mixin
|
42
|
+
def handler_missing(node); end
|
43
|
+
def process(node); end
|
44
|
+
def process_all(nodes); end
|
45
|
+
end
|
46
|
+
module AST::Sexp
|
47
|
+
def s(type, *children); end
|
48
|
+
end
|
@@ -0,0 +1,285 @@
|
|
1
|
+
# This file is autogenerated. Do not edit it by hand. Regenerate it with:
|
2
|
+
# srb rbi gems
|
3
|
+
|
4
|
+
# typed: true
|
5
|
+
#
|
6
|
+
# If you would like to make changes to this file, great! Please create the gem's shim here:
|
7
|
+
#
|
8
|
+
# https://github.com/sorbet/sorbet-typed/new/master?filename=lib/coderay/all/coderay.rbi
|
9
|
+
#
|
10
|
+
# coderay-1.1.3
|
11
|
+
|
12
|
+
module CodeRay
|
13
|
+
def self.coderay_path(*path); end
|
14
|
+
def self.encode(code, lang, format, options = nil); end
|
15
|
+
def self.encode_file(filename, format, options = nil); end
|
16
|
+
def self.encode_tokens(tokens, format, options = nil); end
|
17
|
+
def self.encoder(format, options = nil); end
|
18
|
+
def self.get_scanner_options(options); end
|
19
|
+
def self.highlight(code, lang, options = nil, format = nil); end
|
20
|
+
def self.highlight_file(filename, options = nil, format = nil); end
|
21
|
+
def self.scan(code, lang, options = nil, &block); end
|
22
|
+
def self.scan_file(filename, lang = nil, options = nil, &block); end
|
23
|
+
def self.scanner(lang, options = nil, &block); end
|
24
|
+
end
|
25
|
+
module CodeRay::FileType
|
26
|
+
def self.[](filename, read_shebang = nil); end
|
27
|
+
def self.fetch(filename, default = nil, read_shebang = nil); end
|
28
|
+
def self.type_from_shebang(filename); end
|
29
|
+
end
|
30
|
+
class CodeRay::FileType::UnknownFileType < Exception
|
31
|
+
end
|
32
|
+
class CodeRay::Tokens < Array
|
33
|
+
def begin_group(kind); end
|
34
|
+
def begin_line(kind); end
|
35
|
+
def count; end
|
36
|
+
def encode(encoder, options = nil); end
|
37
|
+
def end_group(kind); end
|
38
|
+
def end_line(kind); end
|
39
|
+
def method_missing(meth, options = nil); end
|
40
|
+
def scanner; end
|
41
|
+
def scanner=(arg0); end
|
42
|
+
def split_into_parts(*sizes); end
|
43
|
+
def text_token(*arg0); end
|
44
|
+
def to_s; end
|
45
|
+
def tokens(*arg0); end
|
46
|
+
end
|
47
|
+
class CodeRay::TokensProxy
|
48
|
+
def block; end
|
49
|
+
def block=(arg0); end
|
50
|
+
def each(*args, &blk); end
|
51
|
+
def encode(encoder, options = nil); end
|
52
|
+
def initialize(input, lang, options = nil, block = nil); end
|
53
|
+
def input; end
|
54
|
+
def input=(arg0); end
|
55
|
+
def lang; end
|
56
|
+
def lang=(arg0); end
|
57
|
+
def method_missing(method, *args, &blk); end
|
58
|
+
def options; end
|
59
|
+
def options=(arg0); end
|
60
|
+
def scanner; end
|
61
|
+
def tokens; end
|
62
|
+
end
|
63
|
+
module CodeRay::PluginHost
|
64
|
+
def [](id, *args, &blk); end
|
65
|
+
def all_plugins; end
|
66
|
+
def const_missing(const); end
|
67
|
+
def default(id = nil); end
|
68
|
+
def list; end
|
69
|
+
def load(id, *args, &blk); end
|
70
|
+
def load_all; end
|
71
|
+
def load_plugin_map; end
|
72
|
+
def make_plugin_hash; end
|
73
|
+
def map(hash); end
|
74
|
+
def path_to(plugin_id); end
|
75
|
+
def plugin_hash; end
|
76
|
+
def plugin_path(*args); end
|
77
|
+
def register(plugin, id); end
|
78
|
+
def self.extended(mod); end
|
79
|
+
def validate_id(id); end
|
80
|
+
end
|
81
|
+
class CodeRay::PluginHost::PluginNotFound < LoadError
|
82
|
+
end
|
83
|
+
class CodeRay::PluginHost::HostNotFound < LoadError
|
84
|
+
end
|
85
|
+
module CodeRay::Plugin
|
86
|
+
def aliases; end
|
87
|
+
def plugin_host(host = nil); end
|
88
|
+
def plugin_id; end
|
89
|
+
def register_for(id); end
|
90
|
+
def title(title = nil); end
|
91
|
+
end
|
92
|
+
module CodeRay::Scanners
|
93
|
+
extend CodeRay::PluginHost
|
94
|
+
end
|
95
|
+
class CodeRay::Scanners::Scanner < StringScanner
|
96
|
+
def binary_string; end
|
97
|
+
def column(pos = nil); end
|
98
|
+
def each(&block); end
|
99
|
+
def file_extension; end
|
100
|
+
def initialize(code = nil, options = nil); end
|
101
|
+
def lang; end
|
102
|
+
def line(pos = nil); end
|
103
|
+
def raise_inspect(message, tokens, state = nil, ambit = nil, backtrace = nil); end
|
104
|
+
def raise_inspect_arguments(message, tokens, state, ambit); end
|
105
|
+
def reset; end
|
106
|
+
def reset_instance; end
|
107
|
+
def scan_rest; end
|
108
|
+
def scan_tokens(tokens, options); end
|
109
|
+
def scanner_state_info(state); end
|
110
|
+
def self.encode_with_encoding(code, target_encoding); end
|
111
|
+
def self.encoding(name = nil); end
|
112
|
+
def self.file_extension(extension = nil); end
|
113
|
+
def self.guess_encoding(s); end
|
114
|
+
def self.lang; end
|
115
|
+
def self.normalize(code); end
|
116
|
+
def self.to_unix(code); end
|
117
|
+
def set_string_from_source(source); end
|
118
|
+
def set_tokens_from_options(options); end
|
119
|
+
def setup; end
|
120
|
+
def state; end
|
121
|
+
def state=(arg0); end
|
122
|
+
def string=(code); end
|
123
|
+
def tokenize(source = nil, options = nil); end
|
124
|
+
def tokens; end
|
125
|
+
def tokens_last(tokens, n); end
|
126
|
+
def tokens_size(tokens); end
|
127
|
+
extend CodeRay::Plugin
|
128
|
+
include Enumerable
|
129
|
+
end
|
130
|
+
class CodeRay::Scanners::Scanner::ScanError < StandardError
|
131
|
+
end
|
132
|
+
class CodeRay::WordList < Hash
|
133
|
+
def add(words, value = nil); end
|
134
|
+
def initialize(default = nil); end
|
135
|
+
end
|
136
|
+
class CodeRay::WordList::CaseIgnoring < CodeRay::WordList
|
137
|
+
def [](key); end
|
138
|
+
def []=(key, value); end
|
139
|
+
end
|
140
|
+
module CodeRay::Scanners::Java::BuiltinTypes
|
141
|
+
end
|
142
|
+
class CodeRay::Scanners::Java < CodeRay::Scanners::Scanner
|
143
|
+
def scan_tokens(encoder, options); end
|
144
|
+
end
|
145
|
+
class CodeRay::Scanners::Ruby < CodeRay::Scanners::Scanner
|
146
|
+
def interpreted_string_state; end
|
147
|
+
def scan_tokens(encoder, options); end
|
148
|
+
def setup; end
|
149
|
+
end
|
150
|
+
module CodeRay::Scanners::Ruby::Patterns
|
151
|
+
end
|
152
|
+
class Anonymous_Struct_1 < Struct
|
153
|
+
def delim; end
|
154
|
+
def delim=(_); end
|
155
|
+
def heredoc; end
|
156
|
+
def heredoc=(_); end
|
157
|
+
def interpreted; end
|
158
|
+
def interpreted=(_); end
|
159
|
+
def next_state; end
|
160
|
+
def next_state=(_); end
|
161
|
+
def opening_paren; end
|
162
|
+
def opening_paren=(_); end
|
163
|
+
def paren_depth; end
|
164
|
+
def paren_depth=(_); end
|
165
|
+
def pattern; end
|
166
|
+
def pattern=(_); end
|
167
|
+
def self.[](*arg0); end
|
168
|
+
def self.inspect; end
|
169
|
+
def self.members; end
|
170
|
+
def self.new(*arg0); end
|
171
|
+
def type; end
|
172
|
+
def type=(_); end
|
173
|
+
end
|
174
|
+
class CodeRay::Scanners::Ruby::StringState < Anonymous_Struct_1
|
175
|
+
def heredoc_pattern(delim, interpreted, indented); end
|
176
|
+
def initialize(kind, interpreted, delim, heredoc = nil); end
|
177
|
+
def self.simple_key_pattern(delim); end
|
178
|
+
end
|
179
|
+
module CodeRay::Encoders
|
180
|
+
extend CodeRay::PluginHost
|
181
|
+
end
|
182
|
+
class CodeRay::Encoders::Encoder
|
183
|
+
def <<(token); end
|
184
|
+
def begin_group(kind); end
|
185
|
+
def begin_line(kind); end
|
186
|
+
def compile(tokens, options = nil); end
|
187
|
+
def encode(code, lang, options = nil); end
|
188
|
+
def encode_tokens(tokens, options = nil); end
|
189
|
+
def end_group(kind); end
|
190
|
+
def end_line(kind); end
|
191
|
+
def file_extension; end
|
192
|
+
def finish(options); end
|
193
|
+
def get_output(options); end
|
194
|
+
def highlight(code, lang, options = nil); end
|
195
|
+
def initialize(options = nil); end
|
196
|
+
def options; end
|
197
|
+
def options=(arg0); end
|
198
|
+
def output(data); end
|
199
|
+
def scanner; end
|
200
|
+
def scanner=(arg0); end
|
201
|
+
def self.const_missing(sym); end
|
202
|
+
def self.file_extension; end
|
203
|
+
def setup(options); end
|
204
|
+
def text_token(text, kind); end
|
205
|
+
def token(content, kind); end
|
206
|
+
def tokens(tokens, options = nil); end
|
207
|
+
extend CodeRay::Plugin
|
208
|
+
end
|
209
|
+
class CodeRay::Encoders::HTML < CodeRay::Encoders::Encoder
|
210
|
+
def begin_group(kind); end
|
211
|
+
def begin_line(kind); end
|
212
|
+
def break_lines(text, style); end
|
213
|
+
def check_group_nesting(name, kind); end
|
214
|
+
def check_options!(options); end
|
215
|
+
def close_span; end
|
216
|
+
def css; end
|
217
|
+
def css_class_for_kinds(kinds); end
|
218
|
+
def end_group(kind); end
|
219
|
+
def end_line(kind); end
|
220
|
+
def finish(options); end
|
221
|
+
def make_span_for_kinds(method, hint); end
|
222
|
+
def self.make_html_escape_hash; end
|
223
|
+
def self.token_path_to_hint(hint, kinds); end
|
224
|
+
def setup(options); end
|
225
|
+
def style_for_kinds(kinds); end
|
226
|
+
def text_token(text, kind); end
|
227
|
+
end
|
228
|
+
module CodeRay::Encoders::HTML::Output
|
229
|
+
def apply_title!(title); end
|
230
|
+
def css; end
|
231
|
+
def css=(arg0); end
|
232
|
+
def self.extended(o); end
|
233
|
+
def self.make_stylesheet(css, in_tag = nil); end
|
234
|
+
def self.page_template_for_css(css); end
|
235
|
+
def stylesheet(in_tag = nil); end
|
236
|
+
def wrap!(element, *args); end
|
237
|
+
def wrap_in!(template); end
|
238
|
+
def wrapped_in; end
|
239
|
+
def wrapped_in=(arg0); end
|
240
|
+
def wrapped_in?(element); end
|
241
|
+
end
|
242
|
+
class CodeRay::Encoders::HTML::Output::Template < String
|
243
|
+
def apply(target, replacement); end
|
244
|
+
def self.wrap!(str, template, target); end
|
245
|
+
end
|
246
|
+
class CodeRay::Encoders::HTML::CSS
|
247
|
+
def get_style_for_css_classes(css_classes); end
|
248
|
+
def initialize(style = nil); end
|
249
|
+
def parse(stylesheet); end
|
250
|
+
def self.load_stylesheet(style = nil); end
|
251
|
+
def stylesheet; end
|
252
|
+
end
|
253
|
+
module CodeRay::Encoders::HTML::Numbering
|
254
|
+
def self.number!(output, mode = nil, options = nil); end
|
255
|
+
end
|
256
|
+
module CodeRay::Styles
|
257
|
+
extend CodeRay::PluginHost
|
258
|
+
end
|
259
|
+
class CodeRay::Styles::Style
|
260
|
+
extend CodeRay::Plugin
|
261
|
+
end
|
262
|
+
class CodeRay::Duo
|
263
|
+
def call(code, options = nil); end
|
264
|
+
def encode(code, options = nil); end
|
265
|
+
def encoder; end
|
266
|
+
def format; end
|
267
|
+
def format=(arg0); end
|
268
|
+
def highlight(code, options = nil); end
|
269
|
+
def initialize(lang = nil, format = nil, options = nil); end
|
270
|
+
def lang; end
|
271
|
+
def lang=(arg0); end
|
272
|
+
def options; end
|
273
|
+
def options=(arg0); end
|
274
|
+
def scanner; end
|
275
|
+
def self.[](*arg0); end
|
276
|
+
end
|
277
|
+
class CodeRay::Encoders::Terminal < CodeRay::Encoders::Encoder
|
278
|
+
def begin_group(kind); end
|
279
|
+
def begin_line(kind); end
|
280
|
+
def end_group(kind); end
|
281
|
+
def end_line(kind); end
|
282
|
+
def open_token(kind); end
|
283
|
+
def setup(options); end
|
284
|
+
def text_token(text, kind); end
|
285
|
+
end
|