linecache-tf 0.44

Sign up to get free protection for your applications and to get access to all the features.
data/NEWS ADDED
@@ -0,0 +1,31 @@
1
+ tf-0.44
2
+ Sept 13, 2010
3
+ - First 1.9.2 release via rb-threadframe
4
+
5
+ 0.43
6
+ 06-12-08
7
+ - tolerance for finding windows extension in lib rather than ext.
8
+
9
+ 0.41
10
+ - add test/data/* to gem.
11
+
12
+ 0.4
13
+ - Credit Ryan Davis and ParseTree.
14
+
15
+ 0.3
16
+ - Add tracelines: get line numbers that can be stopped at.
17
+
18
+ - Add routines to allow line-number
19
+ remapping and filename remapping.
20
+
21
+ - Add access methods to get the number of lines in a file.
22
+
23
+ 0.2
24
+ - Make this work with ruby-debug-base. Add reload-on-change parameters.
25
+ add checkcache, cache, cached? sha1, and stat methods.
26
+
27
+ - Use SCRIPT_LINES__.
28
+
29
+ 0.1
30
+
31
+ - Initial release of LineCache, a module for reading and caching lines.
data/README ADDED
@@ -0,0 +1,48 @@
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, 2009 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.
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env rake
2
+ # -*- Ruby -*-
3
+ require 'rubygems'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/rdoctask'
6
+ require 'rake/testtask'
7
+
8
+ # ------- Default Package ----------
9
+ PKG_VERSION = open(File.join(File.dirname(__FILE__), 'VERSION')) do
10
+ |f| f.readlines[0].chomp
11
+ end
12
+ PKG_NAME = 'linecache'
13
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
14
+ RUBY_FORGE_PROJECT = 'rocky-hacks'
15
+ RUBY_FORGE_USER = 'rockyb'
16
+
17
+ FILES = FileList[
18
+ 'AUTHORS',
19
+ 'COPYING',
20
+ 'ChangeLog',
21
+ 'NEWS',
22
+ 'README',
23
+ 'Rakefile',
24
+ 'VERSION',
25
+ 'lib/*.rb',
26
+ 'test/*.rb',
27
+ 'test/data/*.rb',
28
+ 'test/short-file'
29
+ ]
30
+
31
+ desc "Test everything."
32
+ test_task = task :test => :lib do
33
+ Rake::TestTask.new(:test) do |t|
34
+ t.pattern = 'test/test-*.rb'
35
+ t.verbose = true
36
+ end
37
+ end
38
+
39
+ desc "Test everything - same as test."
40
+ task :check => :test
41
+
42
+ desc "Create a GNU-style ChangeLog via svn2cl"
43
+ task :ChangeLog do
44
+ system("svn2cl svn+ssh://rockyb@rubyforge.org/var/svn/rocky-hacks/linecache/branches/ruby-1.9 --authors=svn2cl_usermap")
45
+ end
46
+
47
+ # Base GEM Specification
48
+ default_spec = Gem::Specification.new do |spec|
49
+ spec.name = "linecache-tf"
50
+
51
+ spec.homepage = "http://rubyforge.org/projects/rocky-hacks/linecache"
52
+ spec.summary = "Read file with caching"
53
+ spec.description = <<-EOF
54
+ LineCache is a module for reading and caching lines. This may be useful for
55
+ example in a debugger where the same lines are shown many times.
56
+
57
+ This version works only with a patched version of Ruby 1.9.2 and rb-threadframe.
58
+ EOF
59
+
60
+ spec.version = "#{PKG_VERSION}"
61
+
62
+ spec.author = "R. Bernstein"
63
+ spec.email = "rockyb@rubyforge.net"
64
+ spec.platform = Gem::Platform::RUBY
65
+ spec.require_path = "lib"
66
+ spec.files = FILES.to_a
67
+
68
+ spec.required_ruby_version = '>= 1.9.2'
69
+ spec.date = Time.now
70
+ spec.add_dependency('rb-threadframe', '>= 0.32')
71
+ spec.rubyforge_project = 'rocky-hacks'
72
+
73
+ # rdoc
74
+ spec.has_rdoc = true
75
+ spec.extra_rdoc_files = %w(README lib/linecache.rb lib/tracelines.rb)
76
+
77
+ spec.test_files = FileList['test/*.rb']
78
+ end
79
+
80
+ # Rake task to build the default package
81
+ Rake::GemPackageTask.new(default_spec) do |pkg|
82
+ pkg.need_tar = true
83
+ end
84
+
85
+ task :default => [:test]
86
+
87
+ # desc "Publish linecache to RubyForge."
88
+ # task :publish do
89
+ # require 'rake/contrib/sshpublisher'
90
+
91
+ # # Get ruby-debug path.
92
+ # ruby_debug_path = File.expand_path(File.dirname(__FILE__))
93
+
94
+ # publisher = Rake::SshDirPublisher.new("rockyb@rubyforge.org",
95
+ # "/var/www/gforge-projects/rocky-hacks/linecache", ruby_debug_path)
96
+ # end
97
+
98
+ desc "Remove built files"
99
+ task :clean => [:clobber_package, :clobber_rdoc]
100
+
101
+ # --------- RDoc Documentation ------
102
+ desc "Generate rdoc documentation"
103
+ Rake::RDocTask.new("rdoc") do |rdoc|
104
+ rdoc.rdoc_dir = 'doc'
105
+ rdoc.title = "linecache"
106
+ # Show source inline with line numbers
107
+ rdoc.options << "--inline-source" << "--line-numbers"
108
+ # Make the readme file the start page for the generated html
109
+ rdoc.options << '--main' << 'README'
110
+ rdoc.rdoc_files.include(%w(lib/*.rb README COPYING))
111
+ end
112
+
113
+ # desc "Publish the release files to RubyForge."
114
+ # task :rubyforge_upload do
115
+ # `rubyforge login`
116
+ # release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} '#{PKG_NAME}-#{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.gem"
117
+ # puts release_command
118
+ # system(release_command)
119
+ # end
120
+
121
+ def install(spec, *opts)
122
+ args = ['gem', 'install', "pkg/#{spec.name}-#{spec.version}.gem"] + opts
123
+ system(*args)
124
+ end
125
+
126
+ desc 'Install locally'
127
+ task :install => :package do
128
+ Dir.chdir(File::dirname(__FILE__)) do
129
+ # ri and rdoc take lots of time
130
+ install(default_spec, '--no-ri', '--no-rdoc')
131
+ end
132
+ end
133
+
134
+ task :install_full => :package do
135
+ Dir.chdir(File::dirname(__FILE__)) do
136
+ install(default_spec)
137
+ end
138
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.44
@@ -0,0 +1,407 @@
1
+ #!/usr/bin/env ruby
2
+ # $Id: linecache.rb 184 2009-10-22 13:05:11Z rockyb $
3
+ #
4
+ # Copyright (C) 2007, 2008, 2009 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
+ # == Version
27
+ # :include:VERSION
28
+
29
+ # == SYNOPSIS
30
+ #
31
+ # The LineCache module allows one to get any line from any file,
32
+ # caching lines of the file on first access to the file. Although the
33
+ # file may be any file, the common use is when the file is a Ruby
34
+ # script since parsing of the file is done to figure out where the
35
+ # statement boundaries are.
36
+ #
37
+ # The routines here may be is useful when a small random sets of lines
38
+ # are read from a single file, in particular in a debugger to show
39
+ # source lines.
40
+ #
41
+ #
42
+ # require 'linecache'
43
+ # lines = LineCache::getlines('/tmp/myruby.rb')
44
+ # # The following lines have same effect as the above.
45
+ # $: << '/tmp'
46
+ # Dir.chdir('/tmp') {lines = LineCache::getlines('myruby.rb')
47
+ #
48
+ # line = LineCache::getline('/tmp/myruby.rb', 6)
49
+ # # Note lines[6] == line (if /tmp/myruby.rb has 6 lines)
50
+ #
51
+ # LineCache::clear_file_cache
52
+ # LineCache::clear_file_cache('/tmp/myruby.rb')
53
+ # LineCache::update_cache # Check for modifications of all cached files.
54
+ #
55
+ # Some parts of the interface is derived from the Python module of the
56
+ # same name.
57
+ #
58
+
59
+ # Defining SCRIPT_LINES__ causes Ruby to cache the lines of files
60
+ # it reads. The key the setting of __FILE__ at the time when Ruby does
61
+ # its read. LineCache keeps a separate copy of the lines elsewhere
62
+ # and never destroys SCRIPT_LINES__
63
+ SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
64
+
65
+ require 'digest/sha1'
66
+ require 'set'
67
+ require_relative 'tracelines'
68
+
69
+ # require 'ruby-debug' ; Debugger.start
70
+
71
+ # = module LineCache
72
+ # A module to read and cache lines of a Ruby program.
73
+ module LineCache
74
+ LineCacheInfo = Struct.new(:stat, :line_numbers, :lines, :path, :sha1) unless
75
+ defined?(LineCacheInfo)
76
+
77
+ # The file cache. The key is a name as would be given by Ruby for
78
+ # __FILE__. The value is a LineCacheInfo object.
79
+ @@file_cache = {}
80
+
81
+ # Maps a string filename (a String) to a key in @@file_cache (a
82
+ # String).
83
+ #
84
+ # One important use of @@file2file_remap is mapping the a full path
85
+ # of a file into the name stored in @@file_cache or given by Ruby's
86
+ # __FILE__. Applications such as those that get input from users,
87
+ # may want canonicalize a file name before looking it up. This map
88
+ # gives a way to do that.
89
+ #
90
+ # Another related use is when a template system is used. Here we'll
91
+ # probably want to remap not only the file name but also line
92
+ # ranges. Will probably use this for that, but I'm not sure.
93
+ @@file2file_remap = {}
94
+ @@file2file_remap_lines = {}
95
+
96
+ # Clear the file cache entirely.
97
+ def clear_file_cache()
98
+ @@file_cache = {}
99
+ @@file2file_remap = {}
100
+ @@file2file_remap_lines = {}
101
+ end
102
+ module_function :clear_file_cache
103
+
104
+ # Return an array of cached file names
105
+ def cached_files()
106
+ @@file_cache.keys
107
+ end
108
+ module_function :cached_files
109
+
110
+ # Discard cache entries that are out of date. If +filename+ is +nil+
111
+ # all entries in the file cache +@@file_cache+ are checked.
112
+ # If we don't have stat information about a file, which can happen
113
+ # if the file was read from __SCRIPT_LINES but no corresponding file
114
+ # is found, it will be kept. Return a list of invalidated filenames.
115
+ # nil is returned if a filename was given but not found cached.
116
+ def checkcache(filename=nil, use_script_lines=false)
117
+
118
+ if !filename
119
+ filenames = @@file_cache.keys()
120
+ elsif @@file_cache.member?(filename)
121
+ filenames = [filename]
122
+ else
123
+ return nil
124
+ end
125
+
126
+ result = []
127
+ for filename in filenames
128
+ next unless @@file_cache.member?(filename)
129
+ path = @@file_cache[filename].path
130
+ if File.exist?(path)
131
+ cache_info = @@file_cache[filename].stat
132
+ stat = File.stat(path)
133
+ if cache_info
134
+ if stat &&
135
+ (cache_info.size != stat.size or cache_info.mtime != stat.mtime)
136
+ result << filename
137
+ update_cache(filename, use_script_lines)
138
+ end
139
+ else
140
+ result << filename
141
+ update_cache(filename, use_script_lines)
142
+ end
143
+ end
144
+ end
145
+ return result
146
+ end
147
+ module_function :checkcache
148
+
149
+ # Cache filename if it's not already cached.
150
+ # Return the expanded filename for it in the cache
151
+ # or nil if we can't find the file.
152
+ def cache(filename, reload_on_change=false)
153
+ if @@file_cache.member?(filename)
154
+ checkcache(filename) if reload_on_change
155
+ else
156
+ update_cache(filename, true)
157
+ end
158
+ if @@file_cache.member?(filename)
159
+ @@file_cache[filename].path
160
+ else
161
+ nil
162
+ end
163
+ end
164
+ module_function :cache
165
+
166
+ # Return true if filename is cached
167
+ def cached?(filename)
168
+ @@file_cache.member?(map_file(filename))
169
+ end
170
+ module_function :cached?
171
+
172
+ def cached_script?(filename)
173
+ SCRIPT_LINES__.member?(map_file(filename))
174
+ end
175
+ module_function :cached_script?
176
+
177
+ def empty?(filename)
178
+ filename=map_file(filename)
179
+ @@file_cache[filename].lines.empty?
180
+ end
181
+ module_function :empty?
182
+
183
+ # Get line +line_number+ from file named +filename+. Return nil if
184
+ # there was a problem. If a file named filename is not found, the
185
+ # function will look for it in the $: array.
186
+ #
187
+ # Examples:
188
+ #
189
+ # lines = LineCache::getline('/tmp/myfile.rb')
190
+ # # Same as above
191
+ # $: << '/tmp'
192
+ # lines = LineCache.getlines('myfile.rb')
193
+ #
194
+ def getline(filename, line_number, reload_on_change=true)
195
+ filename = map_file(filename)
196
+ filename, line_number = map_file_line(filename, line_number)
197
+ lines = getlines(filename, reload_on_change)
198
+ if lines and (1..lines.size) === line_number
199
+ return lines[line_number-1]
200
+ else
201
+ return nil
202
+ end
203
+ end
204
+ module_function :getline
205
+
206
+ # Read lines of +filename+ and cache the results. However +filename+ was
207
+ # previously cached use the results from the cache. Return nil
208
+ # if we can't get lines
209
+ def getlines(filename, reload_on_change=false)
210
+ filename = map_file(filename)
211
+ checkcache(filename) if reload_on_change
212
+ if @@file_cache.member?(filename)
213
+ return @@file_cache[filename].lines
214
+ else
215
+ update_cache(filename, true)
216
+ return @@file_cache[filename].lines if @@file_cache.member?(filename)
217
+ end
218
+ end
219
+ module_function :getlines
220
+
221
+ # Return full filename path for filename
222
+ def path(filename)
223
+ filename = map_file(filename)
224
+ return nil unless @@file_cache.member?(filename)
225
+ @@file_cache[filename].path
226
+ end
227
+ module_function :path
228
+
229
+ def remap_file(to_file, from_file)
230
+ @@file2file_remap[to_file] = from_file
231
+ end
232
+ module_function :remap_file
233
+
234
+ def remap_file_lines(from_file, to_file, range, start)
235
+ range = (range..range) if range.is_a?(Fixnum)
236
+ to_file = from_file unless to_file
237
+ if @@file2file_remap_lines[to_file]
238
+ # FIXME: need to check for overwriting ranges: whether
239
+ # they intersect or one encompasses another.
240
+ @@file2file_remap_lines[to_file] << [from_file, range, start]
241
+ else
242
+ @@file2file_remap_lines[to_file] = [[from_file, range, start]]
243
+ end
244
+ end
245
+ module_function :remap_file_lines
246
+
247
+ # Return SHA1 of filename.
248
+ def sha1(filename)
249
+ filename = map_file(filename)
250
+ return nil unless @@file_cache.member?(filename)
251
+ return @@file_cache[filename].sha1.hexdigest if
252
+ @@file_cache[filename].sha1
253
+ sha1 = Digest::SHA1.new
254
+ @@file_cache[filename].lines.each do |line|
255
+ sha1 << line
256
+ end
257
+ @@file_cache[filename].sha1 = sha1
258
+ sha1.hexdigest
259
+ end
260
+ module_function :sha1
261
+
262
+ # Return the number of lines in filename
263
+ def size(filename)
264
+ filename = map_file(filename)
265
+ return nil unless @@file_cache.member?(filename)
266
+ @@file_cache[filename].lines.length
267
+ end
268
+ module_function :size
269
+
270
+ # Return File.stat in the cache for filename.
271
+ def stat(filename)
272
+ return nil unless @@file_cache.member?(filename)
273
+ @@file_cache[filename].stat
274
+ end
275
+ module_function :stat
276
+
277
+ # Return an Array of breakpoints in filename.
278
+ # The list will contain an entry for each distinct line event call
279
+ # so it is possible (and possibly useful) for a line number appear more
280
+ # than once.
281
+ def trace_line_numbers(filename, reload_on_change=false)
282
+ fullname = cache(filename, reload_on_change)
283
+ return nil unless fullname
284
+ e = @@file_cache[filename]
285
+ unless e.line_numbers
286
+ e.line_numbers =
287
+ TraceLineNumbers.lnums_for_str_array(e.lines)
288
+ e.line_numbers = false unless e.line_numbers
289
+ end
290
+ e.line_numbers
291
+ end
292
+ module_function :trace_line_numbers
293
+
294
+ def map_file(file)
295
+ @@file2file_remap[file] ? @@file2file_remap[file] : file
296
+ end
297
+ module_function :map_file
298
+
299
+ def map_file_line(file, line)
300
+ if @@file2file_remap_lines[file]
301
+ @@file2file_remap_lines[file].each do |from_file, range, start|
302
+ if range === line
303
+ from_file = from_file || file
304
+ return [from_file, start+line-range.begin]
305
+ end
306
+ end
307
+ end
308
+ return [map_file(file), line]
309
+ end
310
+ module_function :map_file_line
311
+
312
+ # Update a cache entry. If something's
313
+ # wrong, return nil. Return true if the cache was updated and false
314
+ # if not. If use_script_lines is true, use that as the source for the
315
+ # lines of the file
316
+ def update_cache(filename, use_script_lines=false)
317
+
318
+ return nil unless filename
319
+
320
+ @@file_cache.delete(filename)
321
+ path = File.expand_path(filename)
322
+
323
+ if use_script_lines
324
+ list = [filename]
325
+ list << @@file2file_remap[path] if @@file2file_remap[path]
326
+ list.each do |name|
327
+ if !SCRIPT_LINES__[name].nil? && SCRIPT_LINES__[name] != true
328
+ begin
329
+ stat = File.stat(name)
330
+ rescue
331
+ stat = nil
332
+ end
333
+ lines = SCRIPT_LINES__[name]
334
+ @@file_cache[filename] = LineCacheInfo.new(stat, nil, lines, path, nil)
335
+ @@file2file_remap[path] = filename
336
+ return true
337
+ end
338
+ end
339
+ end
340
+
341
+ if File.exist?(path)
342
+ stat = File.stat(path)
343
+ elsif File.basename(filename) == filename
344
+ # try looking through the search path.
345
+ stat = nil
346
+ for dirname in $:
347
+ path = File.join(dirname, filename)
348
+ if File.exist?(path)
349
+ stat = File.stat(path)
350
+ break
351
+ end
352
+ end
353
+ return false unless stat
354
+ end
355
+ begin
356
+ fp = File.open(path, 'r')
357
+ lines = fp.readlines()
358
+ fp.close()
359
+ rescue
360
+ ## print '*** cannot open', path, ':', msg
361
+ return nil
362
+ end
363
+ @@file_cache[filename] = LineCacheInfo.new(File.stat(path), nil, lines,
364
+ path, nil)
365
+ @@file2file_remap[path] = filename
366
+ return true
367
+ end
368
+
369
+ module_function :update_cache
370
+
371
+ end
372
+
373
+ # example usage
374
+ if __FILE__ == $0
375
+ def yes_no(var)
376
+ return var ? "" : "not "
377
+ end
378
+
379
+ lines = LineCache::getlines(__FILE__)
380
+ puts "#{__FILE__} has #{LineCache.size(__FILE__)} lines"
381
+ line = LineCache::getline(__FILE__, 6)
382
+ puts "The 6th line is\n#{line}"
383
+ line = LineCache::remap_file(__FILE__, 'another_name')
384
+ puts LineCache::getline('another_name', 7)
385
+
386
+ puts("Files cached: #{LineCache::cached_files.inspect}")
387
+ LineCache::update_cache(__FILE__)
388
+ LineCache::checkcache(__FILE__)
389
+ puts "#{__FILE__} has #{LineCache::size(__FILE__)} lines"
390
+ puts "#{__FILE__} trace line numbers:\n" +
391
+ "#{LineCache::trace_line_numbers(__FILE__).to_a.sort.inspect}"
392
+ puts("#{__FILE__} is %scached." %
393
+ yes_no(LineCache::cached?(__FILE__)))
394
+ puts LineCache::stat(__FILE__).inspect
395
+ puts "Full path: #{LineCache::path(__FILE__)}"
396
+ LineCache::checkcache # Check all files in the cache
397
+ LineCache::clear_file_cache
398
+ puts("#{__FILE__} is now %scached." %
399
+ yes_no(LineCache::cached?(__FILE__)))
400
+ digest = SCRIPT_LINES__.select{|k,v| k =~ /digest.rb$/}
401
+ puts digest.first[0] if digest
402
+ line = LineCache::getline(__FILE__, 7)
403
+ puts "The 7th line is\n#{line}"
404
+ LineCache::remap_file_lines(__FILE__, 'test2', (10..20), 6)
405
+ puts LineCache::getline('test2', 10)
406
+ puts "Remapped 10th line of test2 is\n#{line}"
407
+ end