Class: Audition::Static::SourceFile

Inherits:
Object
  • Object
show all
Defined in:
lib/audition/static/source_file.rb

Overview

A parsed Ruby file plus the magic comments that change Ractor semantics.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source:, path:) ⇒ SourceFile

Returns a new instance of SourceFile.



16
17
18
19
20
# File 'lib/audition/static/source_file.rb', line 16

def initialize(source:, path:)
  @source = source
  @path = path
  @parse_result = Prism.parse(source)
end

Instance Attribute Details

#parse_resultObject (readonly)

Returns the value of attribute parse_result.



10
11
12
# File 'lib/audition/static/source_file.rb', line 10

def parse_result
  @parse_result
end

#pathObject (readonly)

Returns the value of attribute path.



10
11
12
# File 'lib/audition/static/source_file.rb', line 10

def path
  @path
end

#sourceObject (readonly)

Returns the value of attribute source.



10
11
12
# File 'lib/audition/static/source_file.rb', line 10

def source
  @source
end

Class Method Details

.read(path) ⇒ Object



12
13
14
# File 'lib/audition/static/source_file.rb', line 12

def self.read(path)
  new(source: File.read(path), path: path)
end

Instance Method Details

#boot_insertionObject

Where a hoisted require line can be inserted: right after the last existing top-level require, or after the leading comment block (shebang and magic comments), or at the top.



69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/audition/static/source_file.rb', line 69

def boot_insertion
  last_require = root.statements.body.rfind do |s|
    s.is_a?(Prism::CallNode) && s.receiver.nil? &&
      %i[require require_relative].include?(s.name)
  end
  if last_require
    newline = source.index("\n", last_require.location.end_offset)
    offset = newline ? newline + 1 : source.bytesize
    {offset: offset, after_require: true}
  else
    {offset: leading_comments_end, after_require: false}
  end
end

#frozen_string_literal?Boolean

String literals in this file are frozen (and therefore shareable when they contain no interpolation).

Returns:

  • (Boolean)


37
38
39
# File 'lib/audition/static/source_file.rb', line 37

def frozen_string_literal?
  magic_comment("frozen_string_literal") == "true"
end

#leading_comments_endObject



98
99
100
101
102
103
104
105
106
# File 'lib/audition/static/source_file.rb', line 98

def leading_comments_end
  offset = 0
  source.each_line do |line|
    break unless line.start_with?("#")

    offset += line.bytesize
  end
  offset
end

#line_at(number) ⇒ Object



50
51
52
53
# File 'lib/audition/static/source_file.rb', line 50

def line_at(number)
  @lines ||= source.lines
  @lines[number - 1]&.strip
end

#magic_comment(key) ⇒ Object



28
29
30
31
32
33
# File 'lib/audition/static/source_file.rb', line 28

def magic_comment(key)
  comment = parse_result.magic_comments.find do |mc|
    mc.key_loc.slice == key
  end
  comment&.value_loc&.slice
end

#magic_insertion_offsetObject

Where a new magic comment can go: after the shebang and any existing magic comments, before code.



85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/audition/static/source_file.rb', line 85

def magic_insertion_offset
  offset = 0
  if source.start_with?("#!")
    newline = source.index("\n")
    offset = newline ? newline + 1 : source.bytesize
  end
  after_magic = parse_result.magic_comments.map do |mc|
    newline = source.index("\n", mc.value_loc.end_offset)
    newline ? newline + 1 : source.bytesize
  end.max
  [offset, after_magic || 0].max
end

#rootObject



26
# File 'lib/audition/static/source_file.rb', line 26

def root = parse_result.value

#shareable_constants?Boolean

# shareable_constant_value: literal|experimental_everything| experimental_copy makes constant values deeply frozen and shareable at parse time, so constant checks are moot. (v1 treats the comment as file-wide.)

Returns:

  • (Boolean)


45
46
47
48
# File 'lib/audition/static/source_file.rb', line 45

def shareable_constants?
  value = magic_comment("shareable_constant_value")
  !value.nil? && value != "none"
end

#syntax_errorsObject



24
# File 'lib/audition/static/source_file.rb', line 24

def syntax_errors = parse_result.errors

#top_level_requiresObject

Literal feature strings required by top-level statements.



56
57
58
59
60
61
62
63
64
# File 'lib/audition/static/source_file.rb', line 56

def top_level_requires
  @top_level_requires ||= root.statements.body.filter_map do |s|
    next unless s.is_a?(Prism::CallNode) && s.receiver.nil?
    next unless %i[require require_relative].include?(s.name)

    arg = s.arguments&.arguments&.first
    arg.unescaped if arg.is_a?(Prism::StringNode)
  end
end

#valid_syntax?Boolean

Returns:

  • (Boolean)


22
# File 'lib/audition/static/source_file.rb', line 22

def valid_syntax? = parse_result.success?