Class: Ibex::Frontend::SourceLoader

Inherits:
Object
  • Object
show all
Defined in:
lib/ibex/frontend/source_loader.rb,
sig/ibex/frontend/source_loader.rbs

Overview

Resolves canonical grammar paths and reads either open-buffer overlays or disk.

Instance Method Summary collapse

Constructor Details

#initialize(overlays: {}, record_reads: false) ⇒ SourceLoader

Returns a new instance of SourceLoader.

RBS:

  • (?overlays: Hash[String, String], ?record_reads: bool) -> void

Parameters:

  • overlays: (Hash[String, String]) (defaults to: {})
  • record_reads: (Boolean) (defaults to: false)


30
31
32
33
34
35
36
37
# File 'lib/ibex/frontend/source_loader.rb', line 30

def initialize(overlays: {}, record_reads: false)
  @overlays = {} #: Hash[String, String]
  @aliases = {} #: Hash[String, String]
  @read_records = {} #: Hash[String, GenerationInput]
  @record_reads = record_reads
  @access_paths = {} #: Hash[String, Array[String]]
  overlays.each { |path, source| set_overlay(path, source) }
end

Instance Method Details

#canonical_missing_path(expanded) ⇒ String

Resolve the nearest existing ancestor so symlink escapes remain visible for new files.

RBS:

  • (String expanded) -> String

Parameters:

  • expanded (String)

Returns:

  • (String)


131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/ibex/frontend/source_loader.rb', line 131

def canonical_missing_path(expanded)
  raise Errno::ENOENT, expanded if File.symlink?(expanded)

  cursor = expanded
  suffix = [] #: Array[String]
  loop do
    parent = File.dirname(cursor)
    raise Errno::ENOENT, expanded if parent == cursor

    suffix.unshift(File.basename(cursor))
    cursor = parent
    break if File.exist?(cursor) || File.symlink?(cursor)
  end
  raise Errno::ENOTDIR, cursor unless File.directory?(File.realpath(cursor))

  File.join(File.realpath(cursor), *suffix)
end

#canonical_path(path, base: Dir.pwd, allow_missing: false) ⇒ String

Canonicalize an existing file, or a missing path explicitly allowed by an open overlay.

RBS:

  • (String path, ?base: String, ?allow_missing: bool) -> String

Parameters:

  • path (String)
  • base: (String) (defaults to: Dir.pwd)
  • allow_missing: (Boolean) (defaults to: false)

Returns:

  • (String)


41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/ibex/frontend/source_loader.rb', line 41

def canonical_path(path, base: Dir.pwd, allow_missing: false)
  expanded = File.expand_path(path, base)
  aliased = @aliases[expanded]
  return aliased if aliased

  begin
    File.realpath(expanded)
  rescue Errno::ENOENT
    canonical = canonical_missing_path(expanded)
    return canonical if allow_missing || @overlays.key?(canonical)

    raise
  end
end

#delete_overlay(path) ⇒ String

Remove an editor buffer and return its canonical path.

RBS:

  • (String path) -> String

Parameters:

  • path (String)

Returns:

  • (String)


100
101
102
103
104
105
106
# File 'lib/ibex/frontend/source_loader.rb', line 100

def delete_overlay(path)
  expanded = File.expand_path(path)
  canonical = @aliases.delete(expanded) || canonical_path(expanded, allow_missing: true)
  @aliases.delete_if { |_alias_path, target| target == canonical }
  @overlays.delete(canonical)
  canonical
end

#disk_file?(path) ⇒ Boolean

RBS:

  • (String path) -> bool

Parameters:

  • path (String)

Returns:

  • (Boolean)


115
116
117
118
119
# File 'lib/ibex/frontend/source_loader.rb', line 115

def disk_file?(path)
  File.file?(disk_path(path))
rescue SystemCallError
  false
end

#disk_path(path) ⇒ String

Resolve the current disk target without consulting overlay aliases.

RBS:

  • (String path) -> String

Parameters:

  • path (String)

Returns:

  • (String)


123
124
125
# File 'lib/ibex/frontend/source_loader.rb', line 123

def disk_path(path)
  File.realpath(File.expand_path(path))
end

#disk_source(path) ⇒ String

Read disk while deliberately bypassing an open overlay.

RBS:

  • (String path) -> String

Parameters:

  • path (String)

Returns:

  • (String)


110
111
112
# File 'lib/ibex/frontend/source_loader.rb', line 110

def disk_source(path)
  File.binread(disk_path(path))
end

#file?(path) ⇒ Boolean

RBS:

  • (String path) -> bool

Parameters:

  • path (String)

Returns:

  • (Boolean)


71
72
73
74
75
76
# File 'lib/ibex/frontend/source_loader.rb', line 71

def file?(path)
  canonical = canonical_path(path, allow_missing: true)
  @overlays.key?(canonical) || File.file?(canonical)
rescue SystemCallError
  false
end

#overlay?(path) ⇒ Boolean

RBS:

  • (String path) -> bool

Parameters:

  • path (String)

Returns:

  • (Boolean)


79
80
81
82
83
84
# File 'lib/ibex/frontend/source_loader.rb', line 79

def overlay?(path)
  canonical = canonical_path(path, allow_missing: true)
  @overlays.key?(canonical)
rescue SystemCallError
  false
end

#read(path) ⇒ String

RBS:

  • (String path) -> String

Parameters:

  • path (String)

Returns:

  • (String)


57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/ibex/frontend/source_loader.rb', line 57

def read(path)
  canonical = canonical_path(path, allow_missing: true)
  overlay = @overlays[canonical]
  source = overlay || File.binread(canonical)
  if @record_reads
    record = GenerationInput.new(canonical, source)
    (@access_paths[canonical] || []).each { |access| record.add_access_path(access) }
    @read_records[canonical] = record
  end

  source
end

#read_recordsArray[GenerationInput]

RBS:

  • () -> Array[GenerationInput]

Returns:



16
17
18
# File 'lib/ibex/frontend/source_loader.rb', line 16

def read_records
  @read_records.values
end

#record_access(canonical, access_path) ⇒ void

This method returns an undefined value.

Associate the lexical path used by a resolver with its canonical source.

RBS:

  • (String canonical, String access_path) -> void

Parameters:

  • canonical (String)
  • access_path (String)


22
23
24
25
26
27
# File 'lib/ibex/frontend/source_loader.rb', line 22

def record_access(canonical, access_path)
  paths = (@access_paths[canonical] ||= [])
  expanded = File.expand_path(access_path)
  paths << expanded unless paths.include?(expanded)
  @read_records[canonical]&.add_access_path(expanded)
end

#set_overlay(path, source) ⇒ String

Install or replace an editor buffer without writing it to disk.

RBS:

  • (String path, String source) -> String

Parameters:

  • path (String)
  • source (String)

Returns:

  • (String)


88
89
90
91
92
93
94
95
96
# File 'lib/ibex/frontend/source_loader.rb', line 88

def set_overlay(path, source)
  expanded = File.expand_path(path)
  canonical = canonical_path(expanded, allow_missing: true)
  raise ArgumentError, "overlay path must not be a directory" if File.directory?(canonical)

  @aliases[expanded] = canonical
  @overlays[canonical] = source.dup.freeze
  canonical
end