linecache 0.46-java

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.
data/NEWS ADDED
@@ -0,0 +1,39 @@
1
+ 0.46
2
+ 06-12-19
3
+ - A require_relative dependency snuck in.
4
+ Add a rbx-require-relative to handle this.
5
+
6
+ 0.45
7
+ 06-12-11
8
+ - Support for syntax highlighting and caching eval strings. The former
9
+ is used in rb8-trepanning.
10
+
11
+ 0.43
12
+ 06-12-08
13
+ - tolerance for finding windows extension in lib rather than ext.
14
+
15
+ 0.41
16
+ - add test/data/* to gem.
17
+
18
+ 0.4
19
+ - Credit Ryan Davis and ParseTree.
20
+
21
+ 0.3
22
+ - Add tracelines: get line numbers that can be stopped at.
23
+
24
+ - Add routines to allow line-number
25
+ remapping and filename remapping.
26
+
27
+ - Add access methods to get the number of lines in a file.
28
+
29
+ 0.2
30
+ - Make this work with ruby-debug-base. Add reload-on-change parameters.
31
+ add checkcache, cache, cached? sha1, and stat methods.
32
+
33
+ - Use SCRIPT_LINES__.
34
+
35
+ 0.1
36
+
37
+ - Initial release of LineCache, a module for reading and caching lines.
38
+
39
+ $Id$
data/README ADDED
@@ -0,0 +1,50 @@
1
+ = LineCache - A module to read and cache file information of a Ruby program.
2
+
3
+ == SYNOPSIS
4
+
5
+ The LineCache module allows one to get any line from any file, caching
6
+ the lines and file information on first access to the file. Although
7
+ the file may be any file, the common use is when the file is a Ruby
8
+ script since parsing of the file is done to figure out where the
9
+ statement boundaries are.
10
+
11
+ The routines here may be is useful when a small random sets of lines
12
+ are read from a single file, in particular in a debugger to show
13
+ source lines.
14
+
15
+ == Summary
16
+
17
+ require 'linecache'
18
+ lines = LineCache::getlines('/tmp/myruby.rb')
19
+ # The following lines have same effect as the above.
20
+ $: << '/tmp'
21
+ Dir.chdir('/tmp') {lines = LineCache::getlines('myruby.rb')
22
+
23
+ line = LineCache::getline('/tmp/myruby.rb', 6)
24
+ # Note lines[6] == line (if /tmp/myruby.rb has 6 lines)
25
+
26
+ LineCache::clear_file_cache
27
+ LineCache::clear_file_cache('/tmp/myruby.rb')
28
+ LineCache::update_cache # Check for modifications of all cached files.
29
+
30
+ == Credits
31
+
32
+ This is a port of the module of the same name from the Python distribution.
33
+
34
+ The idea for how TraceLineNumbers works, and some code was taken
35
+ from ParseTree by Ryan Davis.
36
+
37
+ == Other stuff
38
+
39
+ Author:: Rocky Bernstein <rockyb@rubyforge.net>
40
+ License:: Copyright (c) 2007, 2008 Rocky Bernstein
41
+ Released under the GNU GPL 2 license
42
+
43
+ == Warranty
44
+
45
+ This program is distributed in the hope that it will be useful,
46
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
47
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48
+ GNU General Public License for more details.
49
+
50
+ $Id$
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env rake
2
+ # -*- Ruby -*-
3
+ require 'rubygems'
4
+ require 'rubygems/package_task'
5
+ require 'rdoc/task'
6
+ require 'rake/testtask'
7
+
8
+ SO_NAME = 'trace_nums.so'
9
+
10
+ ROOT_DIR = File.dirname(__FILE__)
11
+ load File.join %W(#{ROOT_DIR} lib linecache version.rb)
12
+
13
+ PKG_VERSION = LineCache::VERSION
14
+ PKG_NAME = 'linecache'
15
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
16
+ RUBY_FORGE_PROJECT = 'rocky-hacks'
17
+ RUBY_FORGE_USER = 'rockyb'
18
+
19
+ FILES = FileList[
20
+ 'AUTHORS',
21
+ 'COPYING',
22
+ 'ChangeLog',
23
+ 'NEWS',
24
+ 'README',
25
+ 'Rakefile',
26
+ 'ext/linecache/trace_nums.c',
27
+ 'ext/linecache/trace_nums.h',
28
+ 'ext/linecache/extconf.rb',
29
+ 'lib/**/*.rb',
30
+ 'test/*.rb',
31
+ 'test/data/*.rb',
32
+ 'test/short-file'
33
+ ]
34
+
35
+ desc 'Test everything'
36
+ Rake::TestTask.new(:test) do |t|
37
+ t.libs << 'ext'
38
+ t.pattern = 'test/test-*.rb'
39
+ t.options = '--verbose' if $VERBOSE
40
+ end
41
+ task :test => :lib
42
+
43
+ desc 'Create the core ruby-debug shared library extension'
44
+ task :lib do
45
+ Dir.chdir('ext/linecache') do
46
+ system("#{Gem.ruby} extconf.rb && make")
47
+ end
48
+ end
49
+
50
+
51
+ desc 'Test everything - same as test.'
52
+ task :check => :test
53
+
54
+ desc 'Create a GNU-style ChangeLog via svn2cl'
55
+ task :ChangeLog do
56
+ system('git log --pretty --numstat --summary | git2cl > ChangeLog')
57
+ end
58
+
59
+ gem_file = nil
60
+
61
+ # Base GEM Specification
62
+ default_spec = Gem::Specification.new do |spec|
63
+ spec.name = 'linecache'
64
+
65
+ spec.homepage = 'http://rubyforge.org/projects/rocky-hacks/linecache'
66
+ spec.summary = 'Read file with caching'
67
+ spec.description = <<-EOF
68
+ LineCache is a module for reading and caching lines. This may be useful for
69
+ example in a debugger where the same lines are shown many times.
70
+ EOF
71
+
72
+ spec.version = PKG_VERSION
73
+
74
+ spec.author = 'R. Bernstein'
75
+ spec.email = 'rockyb@rubyforge.net'
76
+ spec.platform = Gem::Platform::RUBY
77
+ spec.require_path = 'lib'
78
+ spec.files = FILES.to_a
79
+ spec.extensions = ['ext/linecache/extconf.rb']
80
+
81
+ spec.required_ruby_version = '>= 1.8.7'
82
+ spec.date = Time.now
83
+ spec.rubyforge_project = 'rocky-hacks'
84
+
85
+ # rdoc
86
+ spec.has_rdoc = true
87
+ spec.extra_rdoc_files = ['README', 'lib/linecache.rb', 'lib/linecache/tracelines.rb']
88
+
89
+ spec.test_files = FileList['test/*.rb']
90
+ gem_file = "#{spec.name}-#{spec.version}.gem"
91
+
92
+ end
93
+
94
+ # Rake task to build the default package
95
+ Gem::PackageTask.new(default_spec) do |pkg|
96
+ pkg.need_tar = true
97
+ end
98
+
99
+ task :default => [:test]
100
+
101
+ # Windows specification
102
+ win_spec = default_spec.clone
103
+ win_spec.extensions = []
104
+ ## win_spec.platform = Gem::Platform::WIN32 # deprecated
105
+ win_spec.platform = 'mswin32'
106
+ win_spec.files += ["lib/#{SO_NAME}"]
107
+
108
+ desc 'Create Windows Gem'
109
+ task :win32_gem do
110
+ # Copy the win32 extension the top level directory.
111
+ current_dir = File.expand_path(File.dirname(__FILE__))
112
+ source = File.join(current_dir, 'ext', 'win32', SO_NAME)
113
+ target = File.join(current_dir, 'lib', SO_NAME)
114
+ cp(source, target)
115
+
116
+ # Create the gem, then move it to pkg.
117
+ Gem::Builder.new(win_spec).build
118
+ gem_file = "#{win_spec.name}-#{win_spec.version}-#{win_spec.platform}.gem"
119
+ mv(gem_file, "pkg/#{gem_file}")
120
+
121
+ # Remove win extension from top level directory.
122
+ rm(target)
123
+ end
124
+
125
+ desc 'Publish linecache to RubyForge.'
126
+ task :publish do
127
+ require 'rake/contrib/sshpublisher'
128
+
129
+ # Get ruby-debug path.
130
+ ruby_debug_path = File.expand_path(File.dirname(__FILE__))
131
+
132
+ publisher = Rake::SshDirPublisher.new('rockyb@rubyforge.org',
133
+ '/var/www/gforge-projects/rocky-hacks/linecache', ruby_debug_path)
134
+ end
135
+
136
+ desc 'Remove residue from running patch'
137
+ task :rm_patch_residue do
138
+ FileUtils.rm_rf Dir.glob('**/*.{rej,orig}'), :verbose => true
139
+ end
140
+
141
+ desc 'Remove ~ backup files'
142
+ task :rm_tilde_backups do
143
+ FileUtils.rm_rf Dir.glob('**/*~'), :verbose => true
144
+ end
145
+
146
+ desc 'Remove built files'
147
+ task :clean => [:clobber_package, :clobber_rdoc, :rm_patch_residue,
148
+ :rm_tilde_backups] do
149
+ cd 'ext' do
150
+ if File.exists?('Makefile')
151
+ sh 'make clean'
152
+ rm 'Makefile'
153
+ end
154
+ derived_files = Dir.glob('.o') + Dir.glob('*.so')
155
+ rm derived_files unless derived_files.empty?
156
+ end
157
+ end
158
+
159
+ # --------- RDoc Documentation ------
160
+ desc 'Generate rdoc documentation'
161
+ RDoc::Task.new('rdoc') do |rdoc|
162
+ rdoc.rdoc_dir = 'doc'
163
+ rdoc.title = "linecache #{LineCache::VERSION} Documentation"
164
+ rdoc.options << '--main' << 'README'
165
+ rdoc.rdoc_files.include('ext/**/*.c',
166
+ 'lib/*.rb',
167
+ 'README',
168
+ 'COPYING')
169
+ end
170
+
171
+ desc 'Publish the release files to RubyForge.'
172
+ task :rubyforge_upload do
173
+ `rubyforge login`
174
+ release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} '#{PKG_NAME}-#{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.gem"
175
+ puts release_command
176
+ system(release_command)
177
+ end
178
+
179
+ desc 'Install the gem locally'
180
+ task :install => :gem do
181
+ Dir.chdir(ROOT_DIR) do
182
+ sh %{gem install --local pkg/#{gem_file}}
183
+ end
184
+ end
185
+
186
+ task :install_jruby => :gem do
187
+ namespace :jruby do
188
+ jruby_spec = default_spec.clone
189
+ jruby_spec.platform = "java"
190
+ jruby_spec.files = jruby_spec.files.reject {|f| f =~ /^ext/ }
191
+ jruby_spec.extensions = []
192
+
193
+ Gem::PackageTask.new(jruby_spec) {}
194
+ end
195
+ end
@@ -0,0 +1,565 @@
1
+ #!/usr/bin/env ruby
2
+ # $Id$
3
+ #
4
+ # Copyright (C) 2007, 2008, 2010, 2011 Rocky Bernstein <rockyb@rubyforge.net>
5
+ #
6
+ # This program is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
+ # 02110-1301 USA.
20
+ #
21
+
22
+ # Author:: Rocky Bernstein (mailto:rockyb@rubyforge.net)
23
+ #
24
+ # = linecache
25
+ # A module to read and cache lines of a Ruby program.
26
+
27
+ # == SYNOPSIS
28
+ #
29
+ # The LineCache module allows one to get any line from any file,
30
+ # caching lines of the file on first access to the file. Although the
31
+ # file may be any file, the common use is when the file is a Ruby
32
+ # script since parsing of the file is done to figure out where the
33
+ # statement boundaries are.
34
+ #
35
+ # The routines here may be is useful when a small random sets of lines
36
+ # are read from a single file, in particular in a debugger to show
37
+ # source lines.
38
+ #
39
+ #
40
+ # require 'linecache'
41
+ # lines = LineCache::getlines('/tmp/myruby.rb')
42
+ # # The following lines have same effect as the above.
43
+ # $: << '/tmp'
44
+ # Dir.chdir('/tmp') {lines = LineCache::getlines('myruby.rb')
45
+ #
46
+ # line = LineCache::getline('/tmp/myruby.rb', 6)
47
+ # # Note lines[6] == line (if /tmp/myruby.rb has 6 lines)
48
+ #
49
+ # LineCache::clear_file_cache
50
+ # LineCache::update_cache # Check for modifications of all cached files.
51
+ #
52
+ # Some parts of the interface is derived from the Python module of the
53
+ # same name.
54
+ #
55
+
56
+ # Defining SCRIPT_LINES__ causes Ruby to cache the lines of files
57
+ # it reads. The key the setting of __FILE__ at the time when Ruby does
58
+ # its read. LineCache keeps a separate copy of the lines elsewhere
59
+ # and never destroys SCRIPT_LINES__
60
+ SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
61
+
62
+ require 'digest/sha1'
63
+ require 'set'
64
+
65
+ require 'linecache/tracelines'
66
+ require 'linecache/version'
67
+ # require 'ruby-debug' ; Debugger.start
68
+
69
+ # = module LineCache
70
+ # A module to read and cache lines of a Ruby program.
71
+ module LineCache
72
+ LineCacheInfo = Struct.new(:stat, :line_numbers, :lines, :path, :sha1) unless
73
+ defined?(LineCacheInfo)
74
+
75
+ # The file cache. The key is a name as would be given by Ruby for
76
+ # __FILE__. The value is a LineCacheInfo object.
77
+ @@file_cache = {}
78
+ @@script_cache = {}
79
+
80
+
81
+ # Used for CodeRay syntax highlighting
82
+ @@ruby_highlighter = nil
83
+
84
+ # Maps a string filename (a String) to a key in @@file_cache (a
85
+ # String).
86
+ #
87
+ # One important use of @@file2file_remap is mapping the a full path
88
+ # of a file into the name stored in @@file_cache or given by Ruby's
89
+ # __FILE__. Applications such as those that get input from users,
90
+ # may want canonicalize a file name before looking it up. This map
91
+ # gives a way to do that.
92
+ #
93
+ # Another related use is when a template system is used. Here we'll
94
+ # probably want to remap not only the file name but also line
95
+ # ranges. Will probably use this for that, but I'm not sure.
96
+ @@file2file_remap = {}
97
+ @@file2file_remap_lines = {}
98
+ @@script2file = {}
99
+
100
+ module_function
101
+
102
+ def remove_script_temps
103
+ @@script2file.values.each do |filename|
104
+ File.unlink(filename) if File.exist?(filename)
105
+ end
106
+ end
107
+ at_exit { remove_script_temps }
108
+
109
+
110
+ # Clear the file cache entirely.
111
+ def clear_file_cache(filename=nil)
112
+ if filename
113
+ if @@file_cache[filename]
114
+ @@file_cache.delete(filename)
115
+ end
116
+ else
117
+ @@file_cache = {}
118
+ @@file2file_remap = {}
119
+ @@file2file_remap_lines = {}
120
+ end
121
+ end
122
+
123
+ # Remove syntax-formatted lines in the cache. Use this
124
+ # when you change the CodeRay syntax or Token formatting
125
+ # and want to redo how files may have previously been
126
+ # syntax marked.
127
+ def clear_file_format_cache
128
+ @@file_cache.each_pair do |fname, cache_info|
129
+ cache_info.lines.each_pair do |format, lines|
130
+ next if :plain == format
131
+ @@file_cache[fname].lines[format] = nil
132
+ end
133
+ end
134
+ end
135
+
136
+ # Remove syntax-formatted lines in the cache. Use this
137
+ # when you change the CodeRay syntax or Token formatting
138
+ # and want to redo how files may have previously been
139
+ # syntax marked.
140
+ def clear_file_format_cache
141
+ @@file_cache.each_pair do |fname, cache_info|
142
+ cache_info.lines.each_pair do |format, lines|
143
+ next if :plain == format
144
+ @@file_cache[fname].lines[format] = nil
145
+ end
146
+ end
147
+ end
148
+
149
+ # Clear the script cache entirely.
150
+ def clear_script_cache()
151
+ @@script_cache = {}
152
+ end
153
+
154
+ # Return an array of cached file names
155
+ def cached_files()
156
+ @@file_cache.keys
157
+ end
158
+
159
+ # Discard cache entries that are out of date. If +filename+ is +nil+
160
+ # all entries in the file cache +@@file_cache+ are checked.
161
+ # If we don't have stat information about a file, which can happen
162
+ # if the file was read from __SCRIPT_LINES but no corresponding file
163
+ # is found, it will be kept. Return a list of invalidated filenames.
164
+ # nil is returned if a filename was given but not found cached.
165
+ def checkcache(filename=nil, opts=false)
166
+ use_script_lines =
167
+ if opts.kind_of?(Hash)
168
+ opts[:use_script_lines]
169
+ else
170
+ opts
171
+ end
172
+
173
+ if !filename
174
+ filenames = @@file_cache.keys()
175
+ elsif @@file_cache.member?(filename)
176
+ filenames = [filename]
177
+ else
178
+ return nil
179
+ end
180
+
181
+ result = []
182
+ for filename in filenames
183
+ next unless @@file_cache.member?(filename)
184
+ path = @@file_cache[filename].path
185
+ if File.exist?(path)
186
+ cache_info = @@file_cache[filename].stat
187
+ stat = File.stat(path)
188
+ if cache_info
189
+ if stat &&
190
+ (cache_info.size != stat.size or cache_info.mtime != stat.mtime)
191
+ result << filename
192
+ update_cache(filename, opts)
193
+ end
194
+ else
195
+ result << filename
196
+ update_cache(filename, opts)
197
+ end
198
+ end
199
+ end
200
+ return result
201
+ end
202
+
203
+ # Cache script if it's not already cached.
204
+ def cache_script(script, opts={})
205
+ if !@@script_cache.member?(script)
206
+ update_script_cache(script, opts)
207
+ end
208
+ script
209
+ end
210
+
211
+ # Cache file name or script object if it's not already cached.
212
+ # Return the expanded filename for it in the cache if a filename,
213
+ # or the script, or nil if we can't find the file.
214
+ def cache(file_or_script, reload_on_change=false)
215
+ if file_or_script.kind_of?(String)
216
+ cache_file(file_or_script, reload_on_change)
217
+ else
218
+ cache_script(file_or_script)
219
+ end
220
+ end
221
+
222
+ # Cache filename if it's not already cached.
223
+ # Return the expanded filename for it in the cache
224
+ # or nil if we can't find the file.
225
+ def cache_file(filename, reload_on_change=false, opts={})
226
+ if @@file_cache.member?(filename)
227
+ checkcache(filename) if reload_on_change
228
+ else
229
+ opts[:use_script_lines] = true
230
+ update_cache(filename, opts)
231
+ end
232
+ if @@file_cache.member?(filename)
233
+ @@file_cache[filename].path
234
+ else
235
+ nil
236
+ end
237
+ end
238
+
239
+ # Older routine - for compability.
240
+ # Cache filename if it's not already cached.
241
+ # Return the expanded filename for it in the cache
242
+ # or nil if we can't find the file.
243
+ def cache(filename, reload_on_change=false)
244
+ if @@file_cache.member?(filename)
245
+ checkcache(filename) if reload_on_change
246
+ else
247
+ update_cache(filename, true)
248
+ end
249
+ if @@file_cache.member?(filename)
250
+ @@file_cache[filename].path
251
+ else
252
+ nil
253
+ end
254
+ end
255
+
256
+ # Return true if file_or_script is cached
257
+ def cached?(file_or_script)
258
+ if file_or_script.kind_of?(String)
259
+ @@file_cache.member?(unmap_file(file_or_script))
260
+ else
261
+ cached_script?(file_or_script)
262
+ end
263
+ end
264
+
265
+ def cached_script?(filename)
266
+ SCRIPT_LINES__.member?(unmap_file(filename))
267
+ end
268
+
269
+ def empty?(filename)
270
+ filename=unmap_file(filename)
271
+ !!@@file_cache[filename].lines[:plain]
272
+ end
273
+
274
+ # Get line +line_number+ from file named +filename+. Return nil if
275
+ # there was a problem. If a file named filename is not found, the
276
+ # function will look for it in the $: array.
277
+ #
278
+ # Examples:
279
+ #
280
+ # lines = LineCache::getline('/tmp/myfile.rb')
281
+ # # Same as above
282
+ # $: << '/tmp'
283
+ # lines = LineCache.getlines('myfile.rb')
284
+ #
285
+ def getline(file_or_script, line_number, opts=true)
286
+ reload_on_change =
287
+ if opts.kind_of?(Hash)
288
+ opts[:reload_on_change]
289
+ else
290
+ opts
291
+ end
292
+ lines =
293
+ if file_or_script.kind_of?(String)
294
+ filename = unmap_file(file_or_script)
295
+ filename, line_number = unmap_file_line(filename, line_number)
296
+ getlines(filename, opts)
297
+ else
298
+ script_getlines(file_or_script)
299
+ end
300
+ if lines and (1..lines.size) === line_number
301
+ return lines[line_number-1]
302
+ else
303
+ return nil
304
+ end
305
+ end
306
+
307
+ # Read lines of +filename+ and cache the results. However +filename+ was
308
+ # previously cached use the results from the cache. Return nil
309
+ # if we can't get lines
310
+ def getlines(filename, opts=false)
311
+ if opts.kind_of?(Hash)
312
+ reload_on_change, use_script_lines =
313
+ [opts[:reload_on_change], opts[:use_script_lines]]
314
+ else
315
+ reload_on_change, use_script_lines = [opts, false]
316
+ opts = {:reload_on_change => reload_on_change}
317
+ end
318
+ checkcache(filename) if reload_on_change
319
+ format = opts[:output] || :plain
320
+ if @@file_cache.member?(filename)
321
+ lines = @@file_cache[filename].lines
322
+ if opts[:output] && !lines[format]
323
+ lines[format] =
324
+ highlight_string(lines[:plain].join(''), format).split(/\n/)
325
+ end
326
+ return lines[format]
327
+ else
328
+ opts[:use_script_lines] = true
329
+ update_cache(filename, opts)
330
+ if @@file_cache.member?(filename)
331
+ return @@file_cache[filename].lines[format]
332
+ else
333
+ return nil
334
+ end
335
+ end
336
+ end
337
+
338
+ def highlight_string(string, output_type)
339
+ require 'rubygems'
340
+ begin
341
+ require 'coderay'
342
+ require 'term/ansicolor'
343
+ rescue LoadError
344
+ return string
345
+ end
346
+ @@ruby_highlighter ||= CodeRay::Duo[:ruby, output_type]
347
+ @@ruby_highlighter.encode(string)
348
+ end
349
+
350
+ # Return full filename path for filename
351
+ def path(filename)
352
+ return unless filename.kind_of?(String)
353
+ filename = unmap_file(filename)
354
+ return nil unless @@file_cache.member?(filename)
355
+ @@file_cache[filename].path
356
+ end
357
+
358
+ def remap_file(from_file, to_file)
359
+ @@file2file_remap[from_file] = to_file
360
+ cache_file(to_file)
361
+ end
362
+
363
+ def remap_file_lines(from_file, to_file, range, start)
364
+ range = (range..range) if range.kind_of?(Fixnum)
365
+ to_file = from_file unless to_file
366
+ if @@file2file_remap_lines[to_file]
367
+ # FIXME: need to check for overwriting ranges: whether
368
+ # they intersect or one encompasses another.
369
+ @@file2file_remap_lines[to_file] << [from_file, range, start]
370
+ else
371
+ @@file2file_remap_lines[to_file] = [[from_file, range, start]]
372
+ end
373
+ end
374
+ module_function :remap_file_lines
375
+
376
+ # Return SHA1 of filename.
377
+ def sha1(filename)
378
+ filename = unmap_file(filename)
379
+ return nil unless @@file_cache.member?(filename)
380
+ return @@file_cache[filename].sha1.hexdigest if
381
+ @@file_cache[filename].sha1
382
+ sha1 = Digest::SHA1.new
383
+ @@file_cache[filename].lines[:plain].each do |line|
384
+ sha1 << line + "\n"
385
+ end
386
+ @@file_cache[filename].sha1 = sha1
387
+ sha1.hexdigest
388
+ end
389
+
390
+ # Return the number of lines in filename
391
+ def size(file_or_script)
392
+ cache(file_or_script)
393
+ if file_or_script.kind_of?(String)
394
+ file_or_script = unmap_file(file_or_script)
395
+ return nil unless @@file_cache.member?(file_or_script)
396
+ @@file_cache[file_or_script].lines[:plain].length
397
+ else
398
+ return nil unless @@script_cache.member?(file_or_script)
399
+ @@script_cache[file_or_script].lines[:plain].length
400
+ end
401
+ end
402
+
403
+ # Return File.stat in the cache for filename.
404
+ def stat(filename)
405
+ return nil unless @@file_cache.member?(filename)
406
+ @@file_cache[filename].stat
407
+ end
408
+ module_function :stat
409
+
410
+ # Return an Array of breakpoints in filename.
411
+ # The list will contain an entry for each distinct line event call
412
+ # so it is possible (and possibly useful) for a line number appear more
413
+ # than once.
414
+ def trace_line_numbers(filename, reload_on_change=false)
415
+ fullname = cache(filename, reload_on_change)
416
+ return nil unless fullname
417
+ e = @@file_cache[filename]
418
+ unless e.line_numbers
419
+ e.line_numbers =
420
+ TraceLineNumbers.lnums_for_str_array(e.lines[:plain])
421
+ e.line_numbers = false unless e.line_numbers
422
+ end
423
+ e.line_numbers
424
+ end
425
+
426
+ def unmap_file(file)
427
+ @@file2file_remap[file] ? @@file2file_remap[file] : file
428
+ end
429
+ alias :map_file :unmap_file
430
+
431
+ def unmap_file_line(file, line)
432
+ if @@file2file_remap_lines[file]
433
+ @@file2file_remap_lines[file].each do |from_file, range, start|
434
+ if range === line
435
+ from_file = from_file || file
436
+ return [from_file, start+line-range.begin]
437
+ end
438
+ end
439
+ end
440
+ return [file, line]
441
+ end
442
+
443
+ # Update a cache entry. If something is wrong, return nil. Return
444
+ # true if the cache was updated and false if not.
445
+ def update_script_cache(script, opts)
446
+ # return false unless script_is_eval?(script)
447
+ # string = opts[:string] || script.eval_source
448
+ lines = {:plain => string.split(/\n/)}
449
+ lines[opts[:output]] = highlight_string(string, opts[:output]) if
450
+ opts[:output]
451
+ @@script_cache[script] =
452
+ LineCacheInfo.new(nil, nil, lines, nil, opts[:sha1],
453
+ opts[:compiled_method])
454
+ return true
455
+ end
456
+
457
+ # Update a cache entry. If something's
458
+ # wrong, return nil. Return true if the cache was updated and false
459
+ # if not. If use_script_lines is true, use that as the source for the
460
+ # lines of the file
461
+ def update_cache(filename, opts=false)
462
+ if opts.kind_of?(Hash)
463
+ use_script_lines = opts[:use_script_lines]
464
+ else
465
+ use_script_lines = opts
466
+ opts = {:use_script_lines => use_script_lines}
467
+ end
468
+
469
+ return nil unless filename
470
+
471
+ @@file_cache.delete(filename)
472
+ path = File.expand_path(filename)
473
+
474
+ if use_script_lines
475
+ list = [filename]
476
+ list << @@file2file_remap[path] if @@file2file_remap[path]
477
+ list.each do |name|
478
+ if !SCRIPT_LINES__[name].nil? && SCRIPT_LINES__[name] != true
479
+ begin
480
+ stat = File.stat(name)
481
+ rescue
482
+ stat = nil
483
+ end
484
+ raw_lines = SCRIPT_LINES__[name]
485
+ lines = {:plain => raw_lines}
486
+ lines[opts[:output]] =
487
+ highlight_string(raw_lines.join, opts[:output]).split(/\n/) if
488
+ opts[:output]
489
+ @@file_cache[filename] = LineCacheInfo.new(stat, nil, lines, path, nil)
490
+ @@file2file_remap[path] = filename
491
+ return true
492
+ end
493
+ end
494
+ end
495
+
496
+ if File.exist?(path)
497
+ stat = File.stat(path)
498
+ elsif File.basename(filename) == filename
499
+ # try looking through the search path.
500
+ stat = nil
501
+ for dirname in $:
502
+ path = File.join(dirname, filename)
503
+ if File.exist?(path)
504
+ stat = File.stat(path)
505
+ break
506
+ end
507
+ end
508
+ return false unless stat
509
+ end
510
+ begin
511
+ fp = File.open(path, 'r')
512
+ raw_string = fp.read
513
+ fp.rewind
514
+ lines = {:plain => fp.readlines}
515
+ fp.close()
516
+ lines[opts[:output]] =
517
+ highlight_string(raw_string, opts[:output]).split(/\n/) if
518
+ opts[:output]
519
+ rescue
520
+ ## print '*** cannot open', path, ':', msg
521
+ return nil
522
+ end
523
+ @@file_cache[filename] = LineCacheInfo.new(File.stat(path), nil, lines,
524
+ path, nil)
525
+ @@file2file_remap[path] = filename
526
+ return true
527
+ end
528
+
529
+ end
530
+
531
+ # example usage
532
+ if __FILE__ == $0
533
+ def yes_no(var)
534
+ return var ? "" : "not "
535
+ end
536
+
537
+ lines = LineCache::getlines(__FILE__)
538
+ puts "#{__FILE__} has #{LineCache.size(__FILE__)} lines"
539
+ line = LineCache::getline(__FILE__, 6)
540
+ puts "The 6th line is\n#{line}"
541
+ line = LineCache::remap_file(__FILE__, 'another_name')
542
+ puts LineCache::getline('another_name', 7)
543
+
544
+ puts("Files cached: #{LineCache::cached_files.inspect}")
545
+ LineCache::update_cache(__FILE__)
546
+ LineCache::checkcache(__FILE__)
547
+ puts "#{__FILE__} has #{LineCache::size(__FILE__)} lines"
548
+ puts "#{__FILE__} trace line numbers:\n" +
549
+ "#{LineCache::trace_line_numbers(__FILE__).to_a.sort.inspect}"
550
+ puts("#{__FILE__} is %scached." %
551
+ yes_no(LineCache::cached?(__FILE__)))
552
+ puts LineCache::stat(__FILE__).inspect
553
+ puts "Full path: #{LineCache::path(__FILE__)}"
554
+ LineCache::checkcache # Check all files in the cache
555
+ LineCache::clear_file_cache
556
+ puts("#{__FILE__} is now %scached." %
557
+ yes_no(LineCache::cached?(__FILE__)))
558
+ digest = SCRIPT_LINES__.select{|k,v| k =~ /digest.rb$/}
559
+ puts digest.first[0] if digest
560
+ line = LineCache::getline(__FILE__, 7)
561
+ puts "The 7th line is\n#{line}"
562
+ LineCache::remap_file_lines(__FILE__, 'test2', (10..20), 6)
563
+ puts LineCache::getline('test2', 10)
564
+ puts "Remapped 10th line of test2 is\n#{line}"
565
+ end