rbx-linecache 0.43

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,32 @@
1
+ October 27 2010
2
+ - First gemcutter rubinius release
3
+
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.
32
+
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: README 169 2009-02-08 17:19:33Z rockyb $
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env rake
2
+ # -*- Ruby -*-
3
+ # Are we rubinius? We'll test by checking the specific function we need.
4
+ raise RuntimeError, 'This package is for rubinius only!' unless
5
+ Object.constants.include?('Rubinius') &&
6
+ Rubinius.constants.include?('VM') &&
7
+ Rubinius::VM.respond_to?(:backtrace)
8
+
9
+ require 'rubygems'
10
+ require 'rake/gempackagetask'
11
+ require 'rake/rdoctask'
12
+ require 'rake/testtask'
13
+
14
+ ROOT_DIR = File.dirname(__FILE__)
15
+ require File.join %W(#{ROOT_DIR} lib linecache)
16
+
17
+ def gemspec
18
+ @gemspec ||= eval(File.read('.gemspec'), binding, '.gemspec')
19
+ end
20
+
21
+ desc "Build the gem"
22
+ task :package=>:gem
23
+ task :gem=>:gemspec do
24
+ Dir.chdir(ROOT_DIR) do
25
+ sh "gem build .gemspec"
26
+ FileUtils.mkdir_p 'pkg'
27
+ FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", 'pkg'
28
+ end
29
+ end
30
+
31
+ task :default => [:test]
32
+
33
+ desc 'Install the gem locally'
34
+ task :install => :package do
35
+ Dir.chdir(ROOT_DIR) do
36
+ sh %{gem install --local pkg/#{gemspec.name}-#{gemspec.version}}
37
+ end
38
+ end
39
+
40
+ desc "Create a GNU-style ChangeLog via svn2cl"
41
+ task :ChangeLog do
42
+ system("svn2cl --authors=svn2cl_usermap")
43
+ end
44
+
45
+ desc 'Test units - the smaller tests'
46
+ Rake::TestTask.new(:'test') do |t|
47
+ t.test_files = FileList['test/test-*.rb']
48
+ # t.pattern = 'test/**/*test-*.rb' # instead of above
49
+ t.verbose = true
50
+ end
51
+
52
+ desc "Generate the gemspec"
53
+ task :generate do
54
+ puts gemspec.to_ruby
55
+ end
56
+
57
+ desc "Validate the gemspec"
58
+ task :gemspec do
59
+ gemspec.validate
60
+ end
61
+
62
+ # --------- RDoc Documentation ------
63
+ desc "Generate rdoc documentation"
64
+ Rake::RDocTask.new("rdoc") do |rdoc|
65
+ rdoc.rdoc_dir = 'doc'
66
+ rdoc.title = "rbx-linecache #{LineCache::VERSION} Documentation"
67
+ rdoc.rdoc_files.include(%w(lib/*.rb))
68
+ end
69
+
70
+ desc "Same as rdoc"
71
+ task :doc => :rdoc
72
+
73
+ task :clobber_package do
74
+ FileUtils.rm_rf File.join(ROOT_DIR, 'pkg')
75
+ end
76
+
77
+ task :clobber_rdoc do
78
+ FileUtils.rm_rf File.join(ROOT_DIR, 'doc')
79
+ end
80
+
81
+ desc "Remove built files"
82
+ task :clean => [:clobber_package, :clobber_rdoc]
@@ -0,0 +1,407 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (C) 2007, 2008, 2010 Rocky Bernstein <rockyb@rubyforge.net>
3
+ #
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ # 02110-1301 USA.
18
+ #
19
+
20
+ # Author:: Rocky Bernstein (mailto:rockyb@rubyforge.net)
21
+ #
22
+ # = linecache
23
+ # A module to read and cache lines of a Ruby program.
24
+
25
+ # == SYNOPSIS
26
+ #
27
+ # The LineCache module allows one to get any line from any file,
28
+ # caching lines of the file on first access to the file. Although the
29
+ # file may be any file, the common use is when the file is a Ruby
30
+ # script since parsing of the file is done to figure out where the
31
+ # statement boundaries are.
32
+ #
33
+ # The routines here may be is useful when a small random sets of lines
34
+ # are read from a single file, in particular in a debugger to show
35
+ # source lines.
36
+ #
37
+ #
38
+ # require 'linecache'
39
+ # lines = LineCache::getlines('/tmp/myruby.rb')
40
+ # # The following lines have same effect as the above.
41
+ # $: << '/tmp'
42
+ # Dir.chdir('/tmp') {lines = LineCache::getlines('myruby.rb')
43
+ #
44
+ # line = LineCache::getline('/tmp/myruby.rb', 6)
45
+ # # Note lines[6] == line (if /tmp/myruby.rb has 6 lines)
46
+ #
47
+ # LineCache::clear_file_cache
48
+ # LineCache::clear_file_cache('/tmp/myruby.rb')
49
+ # LineCache::update_cache # Check for modifications of all cached files.
50
+ #
51
+ # Some parts of the interface is derived from the Python module of the
52
+ # same name.
53
+ #
54
+
55
+ # Defining SCRIPT_LINES__ causes Ruby to cache the lines of files
56
+ # it reads. The key the setting of __FILE__ at the time when Ruby does
57
+ # its read. LineCache keeps a separate copy of the lines elsewhere
58
+ # and never destroys SCRIPT_LINES__
59
+ ## SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
60
+
61
+ require 'digest/sha1'
62
+ require 'set'
63
+
64
+ require 'rubygems'
65
+ require 'require_relative'
66
+ require_relative 'tracelines'
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
+ VERSION = '0.43'
73
+ LineCacheInfo = Struct.new(:stat, :line_numbers, :lines, :path, :sha1) unless
74
+ defined?(LineCacheInfo)
75
+
76
+ # The file cache. The key is a name as would be given by Ruby for
77
+ # __FILE__. The value is a LineCacheInfo object.
78
+ @@file_cache = {}
79
+
80
+ # Maps a string filename (a String) to a key in @@file_cache (a
81
+ # String).
82
+ #
83
+ # One important use of @@file2file_remap is mapping the a full path
84
+ # of a file into the name stored in @@file_cache or given by Ruby's
85
+ # __FILE__. Applications such as those that get input from users,
86
+ # may want canonicalize a file name before looking it up. This map
87
+ # gives a way to do that.
88
+ #
89
+ # Another related use is when a template system is used. Here we'll
90
+ # probably want to remap not only the file name but also line
91
+ # ranges. Will probably use this for that, but I'm not sure.
92
+ @@file2file_remap = {}
93
+ @@file2file_remap_lines = {}
94
+
95
+ # Clear the file cache entirely.
96
+ def clear_file_cache()
97
+ @@file_cache = {}
98
+ @@file2file_remap = {}
99
+ @@file2file_remap_lines = {}
100
+ end
101
+ module_function :clear_file_cache
102
+
103
+ # Return an array of cached file names
104
+ def cached_files()
105
+ @@file_cache.keys
106
+ end
107
+ module_function :cached_files
108
+
109
+ # Discard cache entries that are out of date. If +filename+ is +nil+
110
+ # all entries in the file cache +@@file_cache+ are checked.
111
+ # If we don't have stat information about a file, which can happen
112
+ # if the file was read from __SCRIPT_LINES but no corresponding file
113
+ # is found, it will be kept. Return a list of invalidated filenames.
114
+ # nil is returned if a filename was given but not found cached.
115
+ def checkcache(filename=nil, use_script_lines=false)
116
+
117
+ if !filename
118
+ filenames = @@file_cache.keys()
119
+ elsif @@file_cache.member?(filename)
120
+ filenames = [filename]
121
+ else
122
+ return nil
123
+ end
124
+
125
+ result = []
126
+ for filename in filenames
127
+ next unless @@file_cache.member?(filename)
128
+ path = @@file_cache[filename].path
129
+ if File.exist?(path)
130
+ cache_info = @@file_cache[filename].stat
131
+ stat = File.stat(path)
132
+ if cache_info
133
+ if stat &&
134
+ (cache_info.size != stat.size or cache_info.mtime != stat.mtime)
135
+ result << filename
136
+ update_cache(filename, use_script_lines)
137
+ end
138
+ else
139
+ result << filename
140
+ update_cache(filename, use_script_lines)
141
+ end
142
+ end
143
+ end
144
+ return result
145
+ end
146
+ module_function :checkcache
147
+
148
+ # Cache filename if it's not already cached.
149
+ # Return the expanded filename for it in the cache
150
+ # or nil if we can't find the file.
151
+ def cache(filename, reload_on_change=false)
152
+ if @@file_cache.member?(filename)
153
+ checkcache(filename) if reload_on_change
154
+ else
155
+ update_cache(filename, true)
156
+ end
157
+ if @@file_cache.member?(filename)
158
+ @@file_cache[filename].path
159
+ else
160
+ nil
161
+ end
162
+ end
163
+ module_function :cache
164
+
165
+ # Return true if filename is cached
166
+ def cached?(filename)
167
+ @@file_cache.member?(map_file(filename))
168
+ end
169
+ module_function :cached?
170
+
171
+ def cached_script?(filename)
172
+ false
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