Module: BiDiGenerate Private
| Relationships & Source Files | |
| Namespace Children | |
|
Classes:
| |
| Defined in: | rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb, rb/lib/selenium/webdriver/bidi/support/check_generated.rb |
Overview
Generates Ruby WebDriver BiDi protocol modules from the shared, binding-neutral
BiDi schema produced by the JavaScript generator (see PR #17700):
//javascript/selenium-webdriver:create-bidi-src_schema -> bidi-schema.json
The schema is already normalized (inline enums hoisted, unions canonicalized, group composition flattened, wire names and nullability preserved verbatim), so this generator is a straight projection into Ruby with no CDDL interpretation.
Invoked via bazel run //rb/lib/selenium/webdriver:bidi-generate. Bazel passes
the schema path (resolved through runfiles) plus the workspace-relative output
directory as ARGV, and supplies the shared generated-note text as a runfile, so
this is not runnable directly from a source checkout.
Constant Summary
-
BIDI_DOC_URL =
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 42
Companion to the generated
@api privatetags: the page explaining why the BiDi implementation layer is internal and what higher-level API to use instead (see#17628).'https://www.selenium.dev/documentation/warnings/bidi-implementation/' -
INHERITED_INSTANCE_METHODS =
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1277
Public instance methods every accessor would shadow if it reused their name: Domain's own (
execute/initialize) plus everything Object/Kernel expose. The collision guard fails generation before a schema-driven shadow can ship.(%w[execute initialize].to_set + Object.instance_methods.to_set(&:to_s)).freeze
-
LINE_LIMIT =
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 45
RuboCop's Layout/LineLength max; emitted
Serialization::Record.definecalls wrap to stay within it.120 -
PARAMS_CLASS_KINDS =
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1201
::BiDiGenerate::Paramkinds the named args can construct a Parameters object for (record fields, or a union dispatched to one of its variants); anything else forwards a raw hash.%w[record union].freeze
-
RESERVED_FIELD_NAMES =
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 100
Object/Data methods a Data member name would shadow (breaking value semantics or reflection), e.g. a "method" field overriding
Object#method.(RUBY_RESERVED + %w[method hash class send dup clone freeze inspect to_h to_s members with deconstruct deconstruct_keys object_id tap itself then display extensible extensions]).freeze
-
RUBY_RESERVED =
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 48
Ruby keywords that cannot be used as method names unquoted.
%w[begin end rescue ensure raise return yield if unless while until for do case when then class module def].freeze
Class Method Summary
-
.accessor?(type, wrappers, plainly_reached) ⇒ Boolean
Internal use only
A type earns a send-side accessor when it is outbound and not a command param/result wrapper (a command method already builds those).
-
.bidi_only_classes(codes)
Internal use only
Class names among
codesthe classic Error module does not already define — the BiDi-only codes bidi/error.rb registers and whose RBS this file must declare. -
.build_accessors(schema, domain, types)
Internal use only
Outbound-scoped domain accessors: for every emitted type a caller constructs to send, a prefix-free constructor on the Domain subclass.
- .build_command(schema, cmd) Internal use only
- .build_event(schema, event) Internal use only
- .build_ir(schema) Internal use only
- .call(schema_path, output_dir) Internal use only
- .camel_to_snake(str) Internal use only
-
.check!(schema_rootpath)
Internal use only
Verifies the checked-in protocol
.rbmatch what the generator would produce from the current schema — catching a hand-edit or a forgotten regeneration. -
.check_accessor_collisions!(mod)
Internal use only
Fail generation if an accessor name would collide with a command method, an inherited method, or another accessor — turning a future shadow into a build error rather than a silently overridden method.
-
.emit(modules, output_dir, template, extension)
Internal use only
Renders every module through one template and writes the result into target, one file per module.
-
.emit_error_module(schema, output_dir)
Internal use only
Writes protocol/error_code.rb (+ its
.rbs), theProtocol::ErrorCodemap, into the same protocol dir as the generated domain files. -
.enum_const_path(type_name)
Internal use only
Domain-qualified path to an enum's frozen hash constant ("browsingContext.ReadinessState" → "BrowsingContext::READINESS_STATE"), so a generated command method can reference it for an outbound membership check.
-
.enum_key(value)
Internal use only
snake_case hash key for an enum value (only a label for the wire value it maps to).
-
.error_class_name(code)
Internal use only
WebDriver error-code string -> exception class name, matching
Error.for_error'sconvention ("no such node" -> NoSuchNodeError). -
.error_code_map(schema)
Internal use only
The ErrorCode wire values mapped to their Ruby exception class names (schema order), e.g. "no such node" => "NoSuchNodeError".
-
.error_module_current?(schema, protocol_dir) ⇒ Boolean
Internal use only
Whether the checked-in protocol/error_code.rb matches what the generator would render now.
-
.nest_synthetic(types)
Internal use only
The projector tags lifted-out types with
owner, label. -
.rbs_nilable(type)
Internal use only
Makes an RBS type admit nil, idempotently (an already-nilable or opaque type is left as-is).
- .render(mod, template_path) Internal use only
-
.ruby_literal(value)
Internal use only
Source literal for a discriminator/const value (string, boolean, or number).
-
.safe_field_name(name)
Internal use only
Append underscore to a field name that would shadow a core method; the wire name is unaffected, only the Ruby reader is renamed.
-
.safe_method_name(name)
Internal use only
Append underscore to avoid clashing with Ruby reserved keywords.
-
.schema_path(rootpath)
Internal use only
$(rootpath) is relative to the runfiles root; dir anchors us there so it resolves the same way locally and on RBE (an execpath would not).
-
.screaming_snake(camel)
Internal use only
SCREAMING_SNAKE constant name for an enum, matching the EVENTS map style (ReadinessState → READINESS_STATE).
-
.sig_dir(output_dir)
Internal use only
The RBS signatures mirror the source tree under sig/ (the repo's convention), e.g.
- .snake_to_class_name(snake) Internal use only
-
.thread_variant_arg_sigs(schema, types)
Internal use only
Give each union its variants' typed
newsignatures, keyed by factory method name, so rbs_variant_factories can emit a checked signature instead of a splat. -
.type_class_name(type_name)
Internal use only
Local constant for a domain-scoped type: "script.LocalValue" -> "LocalValue".
-
.type_ruby_path(type_name)
Internal use only
Protocol-relative class path: "script.LocalValue" -> "Script::LocalValue".
-
.vendor_accessors(vendor_modules)
Internal use only
An accessor per vendor variant, returning a sibling vendor domain over the same connection (
web_extension.moz->Moz.new(connection)). -
.wrap_call(prefix, args, indent, open: '(', close: ')')
Internal use only
Renders
prefix(args)on one line, or one argument per line when it would exceed LINE_LIMIT at the given indent. - .load_json(path) private Internal use only
- .workspace_root private Internal use only
Class Method Details
.accessor?(type, wrappers, plainly_reached) ⇒ Boolean
A type earns a send-side accessor when it is outbound and not a command param/result
wrapper (a command method already builds those). A nested-away synthetic reached only
as a union arm is excluded — it is built through its union (a variant factory or the
command's flattened dispatch), never standalone. A top-level union variant record keeps
its accessor (the plan constructs it directly, e.g. extension_path), as does a synthetic
reached by a plain field ref (browsingContext.AccessibilityLocator's value).
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1266
def self.accessor?(type, wrappers, plainly_reached) return false unless type.outbound return false if wrappers.include?(type.schema_name) nested_synthetic = !type.union? && type.synthetic !nested_synthetic || plainly_reached.include?(type.schema_name) end
.bidi_only_classes(codes)
Class names among codes the classic Error module does not already define — the BiDi-only codes
bidi/error.rb registers and whose RBS this file must declare. Shared codes already have RBS in
common/error.rbs, so re-declaring them would duplicate the classic signatures. Only the RBS needs
this split; the emitted map (error_code.rb) stays the full self-contained set.
.build_accessors(schema, domain, types)
Outbound-scoped domain accessors: for every emitted type a caller constructs to send,
a prefix-free constructor on the Domain subclass. Built from the pre-nesting type list
so a nested synthetic (referenced by a Ruby-relative Owner::Label path) is reachable.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1250
def self.build_accessors(schema, domain, types) wrappers = schema.command_wrapper_refs(domain) plainly_reached = schema.plainly_reached_types types.select { |t| accessor?(t, wrappers, plainly_reached) }.map do |t| Accessor.new(method_name: safe_method_name(camel_to_snake(type_class_name(t.schema_name))), type_name: schema.domain_relative_path(t.schema_name), union: t.union?, rbs_args: t.union? ? nil : t.rbs_new_args) end end
.build_command(schema, cmd)
[ GitHub ]# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1297
def self.build_command(schema, cmd) params = schema.params_for(cmd['params']) # A param that can't flatten to a typed object (alias or non-record union) would be # silently dropped, so fail generation and handle that shape deliberately if it appears. if cmd['params'] && params.nil? raise "command #{cmd['method']} has params that cannot be expressed as a typed object" end params_ref = cmd['params'] && cmd['params']['ref'] params_kind = schema.type_kind(params_ref) params_class = type_class_name(params_ref) if !params.empty? && PARAMS_CLASS_KINDS.include?(params_kind) Command.new( wire_name: cmd['method'], method_name: safe_method_name(camel_to_snake(cmd['name'])), params: params, result_ref: cmd['result'] && schema.structured_ref(cmd['result']['ref']), params_class: params_class, union_params: params_kind == 'union', spec_href: cmd['specHref'] ) end
.build_event(schema, event)
[ GitHub ]# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1319
def self.build_event(schema, event) params = event['params'] payload_ref = params && params['ref'] && schema.structured_ref(params['ref']) Event.new(wire_name: event['method'], event_name: camel_to_snake(event['name']), payload_ref: payload_ref) end
.build_ir(schema)
[ GitHub ]# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1203
def self.build_ir(schema) schema.domains.map do |domain| types = schema.types_for(domain) thread_variant_arg_sigs(schema, types) vendor_modules = schema.vendor_modules_for(domain) mod = Module.new( name: domain, ruby_class: snake_to_class_name(camel_to_snake(domain)), filename: camel_to_snake(domain), commands: schema.commands_for(domain).map { |cmd| build_command(schema, cmd) }, events: schema.events_for(domain).map { |ev| build_event(schema, ev) }, enums: schema.enums_for(domain), accessors: build_accessors(schema, domain, types) + vendor_accessors(vendor_modules), types: nest_synthetic(types), vendor_modules: vendor_modules, spec_href: schema.domain_href(domain) ) check_accessor_collisions!(mod) mod end end
.call(schema_path, output_dir)
[ GitHub ]# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1347
def self.call(schema_path, output_dir) raw = load_json(schema_path) schema = Schema.new(raw) modules = build_ir(schema) emit(modules, output_dir, 'module.rb.erb', 'rb') emit(modules, sig_dir(output_dir), 'module.rbs.erb', 'rbs') emit_error_module(schema, output_dir) end
.camel_to_snake(str)
[ GitHub ]# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 51
def self.camel_to_snake(str) str .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') .gsub(/([a-z\d])([A-Z])/, '\1_\2') .downcase end
.check!(schema_rootpath)
Verifies the checked-in protocol .rb match what the generator would produce from the
current schema — catching a hand-edit or a forgotten regeneration. Re-renders each module
in memory (no file writes) and compares. The .rbs are covered by Steep.
# File 'rb/lib/selenium/webdriver/bidi/support/check_generated.rb', line 27
def self.check!(schema_rootpath) schema = Schema.new(JSON.parse(File.read(schema_path(schema_rootpath)))) protocol_dir = File.('../protocol', __dir__) template = File.join(__dir__, 'templates', 'module.rb.erb') stale = build_ir(schema).filter_map do |mod| path = File.join(protocol_dir, "#{mod.filename}.rb") "#{mod.filename}.rb" unless File.exist?(path) && File.read(path) == render(mod, template) end stale << 'error_code.rb' unless error_module_current?(schema, protocol_dir) return if stale.empty? warn "Generated BiDi protocol code is stale or hand-edited: #{stale.sort.join(', ')}" warn 'Regenerate with: bazel run //rb/lib/selenium/webdriver:bidi-generate' exit 1 end
.check_accessor_collisions!(mod)
Fail generation if an accessor name would collide with a command method, an inherited method, or another accessor — turning a future shadow into a build error rather than a silently overridden method.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1282
def self.check_accessor_collisions!(mod) commands = mod.commands.to_set(&:method_name) seen = {} mod.accessors.each do |accessor| name = accessor.method_name clash = if commands.include?(name) then 'a command method' elsif INHERITED_INSTANCE_METHODS.include?(name) then 'an inherited method' elsif seen[name] then "the accessor for #{seen[name]}" end raise "accessor #{mod.ruby_class}##{name} collides with #{clash}" if clash seen[name] = accessor.type_name end end
.emit(modules, output_dir, template, extension)
Renders every module through one template and writes the result into target, one file per module. Used for both the Ruby source and its RBS signatures.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1392
def self.emit(modules, output_dir, template, extension) target = File.join(workspace_root, output_dir) FileUtils.mkdir_p(target) tmpl = File.join(File.dirname(__FILE__), 'templates', template) modules.each do |mod| path = File.join(target, "#{mod.filename}.#{extension}") File.write(path, render(mod, tmpl)) warn "bidi-generate: wrote #{path}" end end
.emit_error_module(schema, output_dir)
Writes protocol/error_code.rb (+ its .rbs), the Protocol::ErrorCode map, into the same protocol
dir as the generated domain files.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1374
def self.emit_error_module(schema, output_dir) codes = error_code_map(schema) mod = ErrorModule.new(filename: 'error_code', codes: codes, new_classes: bidi_only_classes(codes)) emit([mod], output_dir, 'error_code.rb.erb', 'rb') emit([mod], sig_dir(output_dir), 'error_code.rbs.erb', 'rbs') end
.enum_const_path(type_name)
Domain-qualified path to an enum's frozen hash constant ("browsingContext.ReadinessState" → "BrowsingContext::READINESS_STATE"), so a generated command method can reference it for an outbound membership check.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 132
def self.enum_const_path(type_name) domain, local = type_name.split('.', 2) "#{snake_to_class_name(camel_to_snake(domain))}::#{screaming_snake(local)}" end
.enum_key(value)
snake_case hash key for an enum value (only a label for the wire value it maps to). Preserves camelCase word boundaries (beforeRequestSent → before_request_sent), maps a leading minus to "neg" (-0 → neg0, -Infinity → neg_infinity; no underscore before a digit, so the key stays normalcase), and collapses other punctuation (dedicated-worker → dedicated_worker).
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 142
def self.enum_key(value) camel_to_snake(value.to_s) .sub(/\A-(?=\d)/, 'neg') .sub(/\A-/, 'neg_') .gsub(/[^a-z0-9]+/, '_') .gsub(/\A_|_\z/, '') end
.error_class_name(code)
WebDriver error-code string -> exception class name, matching Error.for_error's convention
("no such node" -> NoSuchNodeError). The Error suffix is normalized (not doubled) for a code
already ending in "error" ("unknown error" -> UnknownError).
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1368
def self.error_class_name(code) "#{code.split.map(&:capitalize).join.sub(/Error$/, '')}Error" end
.error_code_map(schema)
The ErrorCode wire values mapped to their Ruby exception class names (schema order), e.g. "no such node" => "NoSuchNodeError". This is the schema->Ruby translation: the generated file carries the Ruby names, and a hand-written pass turns them into WebDriverError subclasses under the shared Error namespace. Self-contained — no reference to the classic error module.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1361
def self.error_code_map(schema) schema.error_codes.map { |code| [code, error_class_name(code)] } end
.error_module_current?(schema, protocol_dir) ⇒ Boolean
Whether the checked-in protocol/error_code.rb matches what the generator would render now.
# File 'rb/lib/selenium/webdriver/bidi/support/check_generated.rb', line 45
def self.error_module_current?(schema, protocol_dir) mod = ErrorModule.new(filename: 'error_code', codes: error_code_map(schema)) path = File.join(protocol_dir, 'error_code.rb') File.exist?(path) && File.read(path) == render(mod, File.join(__dir__, 'templates', 'error_code.rb.erb')) end
.load_json(path) (private)
[ GitHub ]# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1410
private_class_method def self.load_json(path) resolved = File.exist?(path) ? path : File.join(Dir.pwd, path) JSON.parse(File.read(resolved)) end
.nest_synthetic(types)
The projector tags lifted-out types with owner, label. Emit each
synthetic record inside its owner's class body under its bare label, so
Owner_Label becomes the nested Owner::Label (refs resolve there via
ruby_path). Synthetic enums stay domain-level. Raises on a missing owner.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1329
def self.nest_synthetic(types) index = types.to_h { |t| [t.schema_name, t] } children = types.select { |t| !t.union? && t.synthetic } children.each do |child| owner = index[child.owner] || raise("synthetic type #{child.schema_name} has no emitted owner #{child.owner}") owner.nested = (owner.nested || []) << child child.ruby_name = child.label end types - children end
.rbs_nilable(type)
Makes an RBS type admit nil, idempotently (an already-nilable or opaque type is left
as-is). Applied to a field whose schema type is nullable, so its value type allows nil;
keyword-optionality is expressed separately by the ? prefix (see rbs_part / rbs_arg).
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 123
def self.rbs_nilable(type) return type if type == 'untyped' || type == 'nil' || type.end_with?('?') "#{type}?" end
.render(mod, template_path)
[ GitHub ]# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1341
def self.render(mod, template_path) generated_note = GeneratedNote.render('#', 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', 'bazel run //rb/lib/selenium/webdriver:bidi-generate') ERB.new(File.read(template_path), trim_mode: '-').result(binding) end
.ruby_literal(value)
Source literal for a discriminator/const value (string, boolean, or number).
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 76
def self.ruby_literal(value) return 'nil' if value.nil? value.is_a?(String) ? "'#{value}'" : value.to_s end
.safe_field_name(name)
Append underscore to a field name that would shadow a core method; the wire name is unaffected, only the Ruby reader is renamed.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 107
def self.safe_field_name(name) # A vendor-prefixed wire name carries a colon (moz:allowPrivateBrowsing); swap it # for an underscore so the Ruby reader is a legal identifier. The wire key is kept. name = name.tr(':', '_') RESERVED_FIELD_NAMES.include?(name) ? "#{name}_" : name end
.safe_method_name(name)
Append underscore to avoid clashing with Ruby reserved keywords.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 94
def self.safe_method_name(name) RUBY_RESERVED.include?(name) ? "#{name}_" : name end
.schema_path(rootpath)
$(rootpath) is relative to the runfiles root; dir anchors us there so it resolves the
same way locally and on RBE (an execpath would not). This file lives at
rb/lib/selenium/webdriver/bidi/support, so expand six levels up to the root — File.expand_path
is separator-agnostic, unlike stripping a "/"-spelled suffix (which would miss on Windows).
The cwd-relative rootpath fallback matches how spec_support's rlocation resolves.
# File 'rb/lib/selenium/webdriver/bidi/support/check_generated.rb', line 56
def self.schema_path(rootpath) runfiles_root = File.('../../../../../..', __dir__) [File.join(runfiles_root, rootpath), rootpath].find { |p| File.exist?(p) } || raise("BiDi schema not found (looked for #{rootpath})") end
.screaming_snake(camel)
SCREAMING_SNAKE constant name for an enum, matching the EVENTS map style (ReadinessState → READINESS_STATE).
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 116
def self.screaming_snake(camel) camel_to_snake(camel).upcase end
.sig_dir(output_dir)
The RBS signatures mirror the source tree under sig/ (the repo's convention), e.g. rb/lib/.../protocol -> rb/sig/lib/.../protocol.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1406
def self.sig_dir(output_dir) output_dir.sub(%r{(\A|/)lib/}, '\1sig/lib/') end
.snake_to_class_name(snake)
[ GitHub ]# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 58
def self.snake_to_class_name(snake) snake.split('_').map(&:capitalize).join end
.thread_variant_arg_sigs(schema, types)
Give each union its variants' typed new signatures, keyed by factory method name, so
rbs_variant_factories can emit a checked signature instead of a splat. Keyed by ruby
path (the form a variant ref carries); a cross-module variant not in this list falls
back to **untyped.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1238
def self.thread_variant_arg_sigs(schema, types) record_sigs = types.reject(&:union?).to_h { |t| [schema.ruby_path_for(t.schema_name), t.rbs_new_args] } types.select(&:union?).each do |union| union.variant_arg_sigs = union.value_variants.to_h do |variant| [BiDiGenerate.enum_key(variant.value), record_sigs[variant.ref]] end end end
.type_class_name(type_name)
Local constant for a domain-scoped type: "script.LocalValue" -> "LocalValue". The first letter is capitalized so a lower-cased spec name (e.g. "permissions.setPermission") still yields a valid Ruby constant.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 65
def self.type_class_name(type_name) type_name.split('.', 2).last.sub(/\A[a-z]/, &:upcase) end
.type_ruby_path(type_name)
Protocol-relative class path: "script.LocalValue" -> "Script::LocalValue".
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 70
def self.type_ruby_path(type_name) domain = type_name.split('.', 2).first "#{snake_to_class_name(camel_to_snake(domain))}::#{type_class_name(type_name)}" end
.vendor_accessors(vendor_modules)
An accessor per vendor variant, returning a sibling vendor domain over the same connection
(web_extension.moz -> Moz.new(connection)). Named after the vendor namespace.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1227
def self.vendor_accessors(vendor_modules) vendor_modules.map do |vendor_module| Accessor.new(method_name: safe_method_name(vendor_module.namespace), type_name: vendor_module.name, union: false, vendor: true) end end
.workspace_root (private)
[ GitHub ]# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1415
private_class_method def self.workspace_root ENV['BUILD_WORKSPACE_DIRECTORY'] || Dir.pwd end
.wrap_call(prefix, args, indent, open: '(', close: ')')
Renders prefix(args) on one line, or one argument per line when it would exceed
LINE_LIMIT at the given indent. open/close default to parentheses (pass {} for a
hash literal) so emitted calls and literals stay within RuboCop's length limit.
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 85
def self.wrap_call(prefix, args, indent, open: '(', close: ')') one_line = "#{prefix}#{open}#{args.join(', ')}#{close}" return one_line if args.empty? || indent + one_line.length <= LINE_LIMIT pad = ' ' * (indent + 2) "#{prefix}#{open}\n#{pad}#{args.join(",\n#{pad}")}\n#{' ' * indent}#{close}" end