module LineCache
def cache(filename, reload_on_change=false)
Return the expanded filename for it in the cache
Cache filename if it's not already cached.
def cache(filename, reload_on_change=false) if @@file_cache.member?(filename) checkcache(filename) if reload_on_change else update_cache(filename, true) end if @@file_cache.member?(filename) @@file_cache[filename].path else nil end end
def cached?(filename)
def cached?(filename) @@file_cache.member?(unmap_file(filename)) end
def cached_files()
def cached_files() @@file_cache.keys end
def cached_script?(filename)
def cached_script?(filename) # In 1.8.6, the SCRIPT_LINES__ filename key can be unqualified # In 1.9.1 it's the fully qualified name if RUBY_VERSION < "1.9" SCRIPT_LINES__.member?(unmap_file(filename)) else SCRIPT_LINES__.member?(File.expand_path(unmap_file(filename))) end end
def checkcache(filename=nil, use_script_lines=false)
is found, it will be kept. Return a list of invalidated filenames.
if the file was read from SCRIPT_LINES__ but no corresponding file
If we don't have stat information about a file, which can happen
all entries in the file cache +@@file_cache+ are checked.
Discard cache entries that are out of date. If +filename+ is +nil+
def checkcache(filename=nil, use_script_lines=false) if !filename filenames = @@file_cache.keys() elsif @@file_cache.member?(filename) filenames = [filename] else return nil end result = [] for filename in filenames next unless @@file_cache.member?(filename) path = @@file_cache[filename].path if File.exist?(path) cache_info = @@file_cache[filename].stat stat = File.stat(path) if stat && (cache_info.size != stat.size or cache_info.mtime != stat.mtime) result << filename update_cache(filename, use_script_lines) end end end return result end
def clear_file_cache()
def clear_file_cache() @@file_cache = {} @@file2file_remap = {} @@file2file_remap_lines = {} end
def empty?(filename)
def empty?(filename) filename=unmap_file(filename) @@file_cache[filename].lines.empty? end
def getline(filename, line_number, reload_on_change=true)
lines = LineCache.getlines('myfile.rb')
$: << '/tmp'
# Same as above
lines = LineCache::getline('/tmp/myfile.rb')
Examples:
function will look for it in the $: array.
there was a problem. If a file named filename is not found, the
Get line +line_number+ from file named +filename+. Return nil if
def getline(filename, line_number, reload_on_change=true) filename = unmap_file(filename) filename, line_number = unmap_file_line(filename, line_number) lines = getlines(filename, reload_on_change) if lines and (1..lines.size) === line_number return lines[line_number-1] else return nil end end
def getlines(filename, reload_on_change=false)
previously cached use the results from the cache. Return nil
Read lines of +filename+ and cache the results. However +filename+ was
def getlines(filename, reload_on_change=false) filename = unmap_file(filename) checkcache(filename) if reload_on_change if @@file_cache.member?(filename) return @@file_cache[filename].lines else update_cache(filename, true) return @@file_cache[filename].lines if @@file_cache.member?(filename) end end
def path(filename)
def path(filename) filename = unmap_file(filename) return nil unless @@file_cache.member?(filename) @@file_cache[filename].path end
def remap_file(from_file, to_file)
def remap_file(from_file, to_file) @@file2file_remap[to_file] = from_file end
def remap_file_lines(from_file, to_file, range, start)
def remap_file_lines(from_file, to_file, range, start) range = (range..range) if range.is_a?(Fixnum) to_file = from_file unless to_file if @@file2file_remap_lines[to_file] # FIXME: need to check for overwriting ranges: whether # they intersect or one encompasses another. @@file2file_remap_lines[to_file] << [from_file, range, start] else @@file2file_remap_lines[to_file] = [[from_file, range, start]] end end
def sha1(filename)
def sha1(filename) filename = unmap_file(filename) return nil unless @@file_cache.member?(filename) return @@file_cache[filename].sha1.hexdigest if @@file_cache[filename].sha1 sha1 = Digest::SHA1.new @@file_cache[filename].lines.each do |line| sha1 << line end @@file_cache[filename].sha1 = sha1 sha1.hexdigest end
def size(filename)
def size(filename) filename = unmap_file(filename) return nil unless @@file_cache.member?(filename) @@file_cache[filename].lines.length end
def stat(filename)
def stat(filename) return nil unless @@file_cache.member?(filename) @@file_cache[filename].stat end
def trace_line_numbers(filename, reload_on_change=false)
so it is possible (and possibly useful) for a line number appear more
The list will contain an entry for each distinct line event call
Return an Array of breakpoints in filename.
def trace_line_numbers(filename, reload_on_change=false) fullname = cache(filename, reload_on_change) return nil unless fullname e = @@file_cache[filename] unless e.line_numbers e.line_numbers = TraceLineNumbers.lnums_for_str_array(e.lines) e.line_numbers = false unless e.line_numbers end e.line_numbers end
def unmap_file(file)
def unmap_file(file) @@file2file_remap[file] ? @@file2file_remap[file] : file end
def unmap_file_line(file, line)
def unmap_file_line(file, line) if @@file2file_remap_lines[file] @@file2file_remap_lines[file].each do |from_file, range, start| if range === line from_file = from_file || file return [from_file, start+line-range.begin] end end end return [file, line] end
def update_cache(filename, use_script_lines=false)
if not. If use_script_lines is true, use that as the source for the
wrong, return nil. Return true if the cache was updated and false
Update a cache entry. If something's
def update_cache(filename, use_script_lines=false) return nil unless filename @@file_cache.delete(filename) path = File.expand_path(filename) if use_script_lines list = [filename] list << @@file2file_remap[path] if @@file2file_remap[path] list.each do |name| if !SCRIPT_LINES__[name].nil? && SCRIPT_LINES__[name] != true begin stat = File.stat(name) rescue stat = nil end lines = SCRIPT_LINES__[name] if "ruby19".respond_to?(:force_encoding) lines.each{|l| l.force_encoding(Encoding.default_external) } end @@file_cache[filename] = LineCacheInfo.new(stat, nil, lines, path, nil) @@file2file_remap[path] = filename return true end end end if File.exist?(path) stat = File.stat(path) elsif File.basename(filename) == filename # try looking through the search path. stat = nil for dirname in $: path = File.join(dirname, filename) if File.exist?(path) stat = File.stat(path) break end end return false unless stat end begin fp = File.open(path, 'r') lines = fp.readlines() fp.close() rescue ## print '*** cannot open', path, ':', msg return nil end @@file_cache[filename] = LineCacheInfo.new(File.stat(path), nil, lines, path, nil) @@file2file_remap[path] = filename return true end